type inference - princeton university computer science...language design for type inference we call...

Post on 04-Oct-2020

0 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

TypeInference

COS326DavidWalker

PrincetonUniversity

slidescopyright2017DavidWalkerpermissiongrantedtoreusetheseslidesfornon-commercialeducaFonalpurposes

MidtermExam

WedOct25,2017InClass(11:00-12:20)

MidtermWeek

Bethereorbesquare!

2

TYPEINFERENCE

3

LanguageDesignforTypeInferenceTheMLlanguageandtypesystemisdesignedtosupportaverystrongformoftypeinference.

LanguageDesignforTypeInferenceTheMLlanguageandtypesystemisdesignedtosupportaverystrongformoftypeinference.MLfindsthistypeformap:

let rec map f l = match l with [ ] -> [ ] | hd::tl -> f hd :: map f tl

map : ('a -> 'b) -> 'a list -> 'b list

LanguageDesignforTypeInferenceTheMLlanguageandtypesystemisdesignedtosupportaverystrongformoftypeinference.MLfindsthistypeformap:whichisreallyanabbreviaFonforthistype:

let rec map f l = match l with [ ] -> [ ] | hd::tl -> f hd :: map f tl

map : ('a -> 'b) -> 'a list -> 'b list

map : forall 'a,'b.('a -> 'b) -> 'a list -> 'b list

LanguageDesignforTypeInferenceWecallthistypetheprincipletype(scheme)formap.AnyotherML-styletypeyoucangivemapisaninstanceofthistype,meaningwecanobtaintheothertypesviasubs3tu3onoftypesforparametersfromtheprincipletype.Eg:

('a -> 'a) -> 'a list -> 'a list

map : ('a -> 'b) -> 'a list -> 'b list

(bool -> int) -> bool list -> int list

('a -> int) -> 'a list -> int list

LanguageDesignforTypeInferencePrincipletypesaregreat:•  thetypeinferenceenginecanmakeabestchoiceforthetypeto

giveanexpression•  theenginedoesn'thavetoguess(andwon'thavetoguesswrong)

ThefactthatprincipletypesexistissurprisinglybriYle.IfyouchangeML'stypesystemaliYlebitineitherdirecFon,itcanfallapart.

LanguageDesignforTypeInferenceSupposewetakeoutpolymorphictypesandneedatypeforid:Thenthecompilermightguessthatidhasone(andonlyone)ofthesetypes:

id : bool -> bool

let id x = x

id : int -> int

LanguageDesignforTypeInferenceSupposewetakeoutpolymorphictypesandneedatypeforid:Thenthecompilermightguessthatidhasone(andonlyone)ofthesetypes:Butlateron,oneofthefollowingcodesnippetswon'ttypecheck:Sowhateverchoiceismade,adifferentonemighthavebeenbeYer.

id true

id : bool -> bool

let id x = x

id : int -> int

id 3

LanguageDesignforTypeInferenceWeshowedthatremovingtypesfromthelanguagecausesafailureofprincipletypes.Doesaddingmoretypesalwaysmaketypeinferenceeasier?

LanguageDesignforTypeInferenceWeshowedthatremovingtypesfromthelanguagecausesafailureofprincipletypes.Doesaddingmoretypesalwaysmaketypeinferenceeasier?

LanguageDesignforTypeInferenceOCamlonlyhasuniversaltypesontheoutside:Considerthisprogram:Itwon'ttypecheckinOCaml.Wemightwanttogiveitthistype:NoFcethattheuniversalquanFfierappearsunderan->.

f : (forall a.a->a) -> bool * int

forall 'a,'b. ('a -> 'b) -> 'a list -> 'b list

let f g = (g true, g 3)

LanguageDesignforTypeInferenceSystemFisalotlikeOCaml,exceptthatitallowsuniversalquanFfiersinanyposiFon.Itcouldtypecheckf.Unfortunately,typeinferenceinSystemFisundecideable..

f : (forall a.a->a) -> bool * int

let f g = (g true, g 3)

LanguageDesignforTypeInferenceSystemFisalotlikeOCaml,exceptthatitallowsuniversalquanFfiersinanyposiFon.Itcouldtypecheckf.Unfortunately,typeinferenceinSystemFisundecideable.Developedin1972bylogicianJeanYves-Girardwhowasinterestedintheconsistencyofalogicof2nd-orderarithemeFc.RediscoveredasprogramminglanguagebyJohnReynoldsin1974..

f : (forall a.a->a) -> bool * int

let f g = (g true, g 3)

LanguageDesignforTypeInferenceEvenseeminglysmallchangescaneffecttypeinference.Suppose"+"operatedonbothfloatsandints.Whattypeforthis?

let f x = x + x

LanguageDesignforTypeInferenceEvenseeminglysmallchangescaneffecttypeinference.Suppose"+"operatedonbothfloatsandints.Whattypeforthis?

f : int -> int ?

let f x = x + x

f : float -> float ?

LanguageDesignforTypeInferenceEvenseeminglysmallchangescaneffecttypeinference.Suppose"+"operatedonbothfloatsandints.Whattypeforthis?

f : int -> int ?

let f x = x + x

f : float -> float ?

f : 'a -> 'a ?

LanguageDesignforTypeInferenceEvenseeminglysmallchangescaneffecttypeinference.Suppose"+"operatedonbothfloatsandints.Whattypeforthis?NotypeinOCaml'stypesystemworks.InHaskell:

f : int -> int ?

let f x = x + x

f : float -> float ?

f : 'a -> 'a ?

f : Num 'a => 'a -> 'a

INFERRINGSIMPLETYPES

20

TypeSchemesAtypeschemecontainstypevariablesthatmaybefilledinduringtypeinference

s::=a|int|bool|s->s

Atermschemeisatermthatcontainstypeschemesratherthanpropertypes.eg,forfuncFons:

fun(x:s)->e

letrecf(x:s):s=e

TheGenericTypeInferenceAlgorithm1)AdddisFnctvariablesinallplacestypeschemesareneeded

22

TheGenericTypeInferenceAlgorithm1)AdddisFnctvariablesinallplacestypeschemesareneeded2)Generateconstraints(equaFonsbetweentypes)thatmustbesaFsfiedinorderforanexpressiontotypecheck

•  NoFcethedifferencebetweenthisandthetypecheckingalgorithmfromlastFme.LastFme,wetriedto:•  eagerlydeducetheconcretetypewhencheckingeveryexpression•  rejectprogramswhentypesdidn'tmatch.eg:

•  ThisFme,we'llcollectupequaFonslike:

23

fe--f'sargumenttypemustequale

a->b=c

TheGenericTypeInferenceAlgorithm1)AdddisFnctvariablesinallplacestypeschemesareneeded2)Generateconstraints(equaFonsbetweentypes)thatmustbesaFsfiedinorderforanexpressiontotypecheck

•  NoFcethedifferencebetweenthisandthetypecheckingalgorithmfromlastFme.LastFme,wetriedto:•  eagerlydeducetheconcretetypewhencheckingeveryexpression•  rejectprogramswhentypesdidn'tmatch.eg:

•  ThisFme,we'llcollectupequaFonslike:

3)SolvetheequaFons,generaFngsubsFtuFonsoftypesforvar's

24

fe--f'sargumenttypemustequale

a->b=c

Example:Inferringtypesformap

let rec map f l = match l with

[] -> [] | hd::tl -> f hd :: map f tl

Step1:Annotate

let rec map (f:a) (l:b) : c = match l with

[] -> [] | hd::tl -> f hd :: map f tl

Step2:GenerateConstraints

let rec map (f:a) (l:b) : c = match l with

[] -> [] | hd::tl -> f hd :: map f tl b = d list

a = d -> f

...

Step2:GenerateConstraints

let rec map (f:a) (l:b) : c = match l with

[] -> [] | hd::tl -> f hd :: map f tl

b = b’ list b = b’’ list

b = b’’’ list

a = a

b = b’’’ list

a = b’’ -> a’

c = c’ list

a’ = c’

d list = c’ list

d list = c

finalconstraints:

Step3:SolveConstraints

let rec map (f:a) (l:b) : c = match l with

[] -> [] | hd::tl -> f hd :: map f tl

b = b’ list b = b’’ list

b = b’’’ list

a = a

b = b’’’ list

a = b’’ -> a’

c = c’ list

a’ = c’

d list = c’ list

d list = c

finalconstraints:[b' -> c'/a] [b' list/b]

[c' list/c]

finalsoluFon:

Step3:SolveConstraints

let rec map (f:a) (l:b) : c = match l with

[] -> [] | hd::tl -> f hd :: map f tl

[b' -> c'/a] [b' list/b]

[c' list/c]

finalsoluFon:

let rec map (f:b' -> c') (l:b' list) : c' list = match l with

[] -> [] | hd::tl -> f hd :: map f tl

Step3:SolveConstraints

let rec map (f:a) (l:b) : c = match l with

[] -> [] | hd::tl -> f hd :: map f tl

let rec map (f:a -> b) (l:a list) : b list = match l with

[] -> [] | hd::tl -> f hd :: map f tl

renamingtypevariables:

Step4:GeneratetypesGeneratetypesfromtypeschemes

–  OpFon1:pickaninstanceofthemostgeneraltypewhenwehavecompletedtypeinferenceontheenFreprogram

•  map:(int->int)->intlist->intlist

–  OpFon2:generatepolymorphictypesforprogrampartsandconFnue(polymorphic)typeinference

•  map:foralla,b,c.(a->b)->alist->blist

TypeInferenceDetailsTypeconstraintsaresetsofequaFonsbetweentypeschemes

–  q::={s11=s12,...,sn1=sn2}

–  eg:{b=b’list,a=b->c}

ConstraintGeneraFonSyntax-directedconstraintgeneraFon

–  ouralgorithmcrawlsoverabstractsyntaxofuntypedexpressionsandgenerates

•  atermscheme•  asetofconstraints

Algorithmdefinedassetofinferencerules:

–  G|--u=>e:t,q

contextannotatedexpressionunannotated

expression

type(scheme)

constraintsthatmustbesolved

gen:ctxt->exp->ann_exp*scheme*constraintsinOCaml:

ConstraintGeneraFonSimplerules:

–  G|--x==>x:s,{}(ifG(x)=s)

–  G|--3==>3:int,{}(sameforotherints)

–  G|--true==>true:bool,{}

–  G|--false==>false:bool,{}

Operators

G |-- u1 ==> e1 : t1, q1 G |-- u2 ==> e2 : t2, q2 ------------------------------------------------------------------------ G |-- u1 + u2 ==> e1 + e2 : int, q1 U q2 U {t1 = int, t2 = int}

G |-- u1 ==> e1 : t1, q1 G |-- u2 ==> e2 : t2, q2 ------------------------------------------------------------------------ G |-- u1 < u2 ==> e1 + e2 : bool, q1 U q2 U {t1 = int, t2 = int}

Ifstatements

G |-- u1 ==> e1 : t1, q1 G |-- u2 ==> e2 : t2, q2 G |-- u3 ==> e3 : t3, q3 ---------------------------------------------------------------- G |-- if u1 then u2 else u3 ==> if e1 then e2 else e3 : a, q1 U q2 U q3 U {t1 = bool, a = t2, a = t3}

FuncFonApplicaFon

G |-- u1 ==> e1 : t1, q1 G |-- u2 ==> e2 : t2, q2 (for a fresh a) ---------------------------------------------------------------- G |-- u1 u2==> e1 e2 : a, q1 U q2 U {t1 = t2 -> a}

FuncFonDeclaraFon

G, x : a |-- u ==> e : t, q (for fresh a) ---------------------------------------------------------------- G |-- fun x -> e ==> fun (x : a) -> e : a -> b, q U {t = b}

FuncFonDeclaraFon

G, f : a -> b, x : a |-- u ==> e : t, q (for fresh a,b) ----------------------------------------------------------------------- G |-- rec f(x) = u ==> rec f (x : a) : b = e : a -> b, q U {t = b}

SolvingConstraints

AsoluFontoasystemoftypeconstraintsisasubs3tu3onS–  afuncFonfromtypevariablestotypes–  assumesubsFtuFonsaredefinedonalltypevariables:

•  S(a)=a(foralmostallvariablesa)•  S(a)=s(forsometypeschemes)

–  dom(S)=setofvariabless.t.S(a)≠a

SolvingConstraints

AsoluFontoasystemoftypeconstraintsisasubs3tu3onS–  afuncFonfromtypevariablestotypes–  assumesubsFtuFonsaredefinedonalltypevariables:

•  S(a)=a(foralmostallvariablesa)•  S(a)=s(forsometypeschemes)

–  dom(S)=setofvariabless.t.S(a)≠a

WecanalsoapplyasubsFtuFonStoafulltypeschemes. apply:[int/a,int->bool/b] to:b->a->b returns:(int->bool)->int->(int->bool)

SubsFtuFons

WecanapplyasubsFtuFonStoafulltypescheme:eg:apply[int/a,int->bool/b]tob->a->breturns:(int->bool)->int->(int->bool)

SubsFtuFons

WhenisasubsFtuFonSasoluFontoasetofconstraints?Constraints:{s1=s2,s3=s4,s5=s6,...}WhenthesubsFtuFonmakesbothsidesofallequaFonsthesame.Eg:

a=b->cc=int->bool

constraints:

SubsFtuFons

WhenisasubsFtuFonSasoluFontoasetofconstraints?Constraints:{s1=s2,s3=s4,s5=s6,...}WhenthesubsFtuFonmakesbothsidesofallequaFonsthesame.Eg:

a=b->cc=int->bool

b->(int->bool)/aint->bool/cb/b

constraints:

soluFon:

SubsFtuFons

WhenisasubsFtuFonSasoluFontoasetofconstraints?Constraints:{s1=s2,s3=s4,s5=s6,...}WhenthesubsFtuFonmakesbothsidesofallequaFonsthesame.Eg:

a=b->cc=int->bool

b->(int->bool)/aint->bool/cb/b

b->(int->bool)=b->(int->bool)int->bool=int->bool

constraints:

soluFon:

constraintswithsoluFonapplied:

SubsFtuFons

WhenisasubsFtuFonSasoluFontoasetofconstraints?Constraints:{s1=s2,s3=s4,s5=s6,...}WhenthesubsFtuFonmakesbothsidesofallequaFonsthesame.AsecondsoluFon

a=b->cc=int->bool

b->(int->bool)/aint->bool/cb/b

constraints:

soluFon1:

int->(int->bool)/aint->bool/cint/b

soluFon2:

SubsFtuFons

WhenisonesoluFonbeYerthananothertoasetofconstraints?

a=b->cc=int->bool

b->(int->bool)/aint->bool/cb/b

constraints:

soluFon1:int->(int->bool)/aint->bool/cint/b

soluFon2:

b->(int->bool)

typeb->cwithsoluFonapplied:

int->(int->bool)

typeb->cwithsoluFonapplied:

SubsFtuFons

SoluFon1is"moregeneral"–thereismoreflex.SoluFon2is"moreconcrete"WeprefersoluFon1.

b->(int->bool)/aint->bool/cb/b

soluFon1:int->(int->bool)/aint->bool/cint/b

soluFon2:

b->(int->bool)

typeb->cwithsoluFonapplied:

int->(int->bool)

typeb->cwithsoluFonapplied:

SubsFtuFons

SoluFon1is"moregeneral"–thereismoreflex.SoluFon2is"moreconcrete"Wepreferthemoregeneral(lessconcrete)soluFon1.Technically,wepreferTtoSifthereexistsanothersubsFtuFonUandforalltypest,S(t)=U(T(t))

b->(int->bool)/aint->bool/cb/b

soluFon1:int->(int->bool)/aint->bool/cint/b

soluFon2:

b->(int->bool)

typeb->cwithsoluFonapplied:

int->(int->bool)

typeb->cwithsoluFonapplied:

SubsFtuFons

ThereisalwaysabestsoluFon,whichwecanaprinciplesolu3on.ThebestsoluFonis(atleastas)preferredasanyothersoluFon.

b->(int->bool)/aint->bool/cb/b

soluFon1:int->(int->bool)/aint->bool/cint/b

soluFon2:

b->(int->bool)

typeb->cwithsoluFonapplied:

int->(int->bool)

typeb->cwithsoluFonapplied:

MostGeneralSoluFons

Sistheprincipal(mostgeneral)soluFonofaconstraintqif–  S|=q(itisasoluFon)–  ifT|=qthenT<=S(itisthemostgeneralone)

Lemma:IfqhasasoluFon,thenithasamostgeneraloneWecareaboutprincipalsoluFonssincetheywillgiveusthemostgeneraltypesforterms

ComposiFonofSubsFtuFonsWewillneedtocomparesubsFtuFons:T<=S.eg:

–  T<=SifTis“morespecific”/"less general"thanS–  Ifthereisa

–  Formally:T<=SifandonlyifT=UoSforsomeU

ComposiFonofSubsFtuFonsComposiFon(UoS)appliesthesubsFtuFonSandthenappliesthesubsFtuFonU:

–  (UoS)(a)=U(S(a))WewillneedtocomparesubsFtuFons

–  T<=SifTis“morespecific”thanS–  T<=SifTis“lessgeneral”thanS–  Formally:T<=SifandonlyifT=UoSforsomeU

ComposiFonofSubsFtuFonsExamples:

–  example1:anysubsFtuFonislessgeneralthantheidenFtysubsFtuFonI:

•  S<=IbecauseS=SoI–  example2:

•  S(a)=int,S(b)=c->c•  T(a)=int,T(b)=int->int•  weconclude:T<=S•  ifT(a)=int,T(b)=int->boolthenTisunrelatedtoS(neithermorenorlessgeneral)

SolvingaConstraint

S|=qifSisasoluFontotheconstraintsq

S(s1) = S(s2) S |= q ----------------------------------- S |= {s1 = s2} U q

---------- S |= { }

anysubsFtuFonisasoluFonfortheemptysetofconstraints

asoluFontoanequaFonisasubsFtuFonthatmakesletandrightsidesequal

ExamplesExample1

–  q={a=int,b=a}–  principalsoluFonS:

ExamplesExample1

–  q={a=int,b=a}–  principalsoluFonS:

•  S(a)=S(b)=int•  S(c)=c(forallcotherthana,b)

ExamplesExample2

–  q={a=int,b=a,b=bool}–  principalsoluFonS:

ExamplesExample2

–  q={a=int,b=a,b=bool}–  principalsoluFonS:

•  doesnotexist(thereisnosoluFontoq)

UnificaFonUnificaFon:AnalgorithmthatprovidestheprincipalsoluFontoasetofconstraints(ifoneexists)

–  UnificaFonsystemaFcallysimplifiesasetofconstraints,yieldingasubsFtuFon

•  StarFngstateofunificaFonprocess:(I,q)•  FinalstateofunificaFonprocess:(S,{})

UnificaFonMachineWecanspecifyunificaFonasatransiFonsystem:Basetypes&simplevariables:

(S,{bool=bool} U q) -> (S, q)

(S,{a=a} U q) -> (S, q)

(S,{int=int} U q) -> (S, q)

(S1, q1) -> (S2, q2)

UnificaFonMachineFuncFons:VariabledefiniFons

(S, {s11 -> s12 = s21 -> s22} U q) -> (S, {s11 = s21, s12 = s22} U q)

(S, {a=s} U q) -> ([a=s] o S, [s/a]q)

(S, {s=a} U q) -> ([a=s] o S, [s/a]q)

when a is not in FreeVars(s)

breaksdownintosmallerconstraints

do S and then do [a=b]

OccursCheck

Recallthisprogram:Itgeneratesthetheconstraints:a->a=aWhatisthesoluFonto{a=a->a}?

funx->xx

OccursCheck

Recallthisprogram:Itgeneratesthetheconstraints:a->a=aWhatisthesoluFonto{a=a->a}?Thereisnone!

the"occurscheck"

(S, {s=a} U q) -> ([a=s] o S, [s/a]q)

"when a is not in FreeVars(s)"

funx->xx

IrreducibleStatesWhenalltheconstraintshavebeenprocessed,wewin!ButsomeFmeswegetstuck,withanequaFonlikethis

–  int=bool–  s1->s2=int–  s1->s2=bool–  a=s(scontainsa)–  orissymmetrictooneoftheabove

Stuckstatesarisewhenconstraintsareunsolvable&theprogramdoesnottypecheck.

(S1, q1) -> (S2, q2) -> (S3, q3) ... -> (Sn, { })

TerminaFonWewantunificaFontoterminate(togiveusatypereconstrucFonalgorithm)Inotherwords,wewanttoshowthatthereisnoinfinitesequenceofstates

–  (S1,q1)->(S2,q2)->...

TerminaFon

Weassociateanorderingwithconstraints–  q<q’ifandonlyif

•  qcontainsfewervariablesthanq’•  qcontainsthesamenumberofvariablesasq’butfewertypeconstructors(ie:feweroccurrencesofint,bool,or“->”)

–  Thisisalexacographicordering•  wecanprove(bycontradicFon)thatthereisnoinfinitedecreasingsequenceofconstraints

TerminaFon

Lemma:Everystepreducesthesizeofq–  Proof:Bycases(ie:inducFon)onthedefiniFonofthereducFonrelaFon.

-------------------------------- (S,{int=int} U q) -> (S, q) ------------------------------------ (S,{bool=bool} U q) -> (S, q)

----------------------------- (S,{a=a} U q) -> (S, q)

---------------------------------------------- (S,{s11 -> s12= s21 -> s22} U q) -> (S, {s11 = s21, s12 = s22} U q)

------------------------ (a not in FV(s)) (S,{a=s} U q) -> ([a=s] o S, [s/a]q)

CompleteSoluFonsAcompletesoluFonfor(S,q)isasubsFtuFonTsuchthat

–  T<=S–  T|=q

ProperFesofSoluFonsLemma1:

–  Everyfinalstate(S,{})hasacompletesoluFon.•  ItisS:

–  S<=S–  S|={}

ProperFesofSoluFonsLemma2

–  NostuckstatehasacompletesoluFon(oranysoluFonatall)•  itisimpossibleforasubsFtuFontomakethenecessaryequaFonsequal

–  int≠bool–  int≠t1->t2–  ...

ProperFesofSoluFonsLemma3

–  If(S,q)->(S’,q’)then•  Tiscompletefor(S,q)iffTiscompletefor(S’,q’)•  proofby?•  intheforwarddirecFon,thisisthepreservaFontheoremfortheunificaFonmachine!

Summary:UnificaFonByterminaFon,(I,q)->*(S,q’)where(S,q’)isirreducible.Moreover:

–  Ifq’={}then•  (S,q’)isfinal(bydefiniFon)•  SisaprincipalsoluFonforq

–  ConsideranyTsuchthatTisasoluFontoq.–  NownoFce,Siscompletefor(S,q’)(bylemma1)–  Siscompletefor(I,q)(bylemma3)–  SinceSiscompletefor(I,q),T<=SandthereforeSisprincipal.

Summary:UnificaFon(cont.)...Moreover:

–  Ifq’isnot{}(and(I,q)->*(S,q’)where(S,q’)isirreducible)then

•  (S,q)isstuck.Consequently,(S,q)hasnocompletesoluFon.Bylemma3,even(I,q)hasnocompletesoluFonandthereforeqhasnosoluFonatall.

MORETYPEINFERENCE

76

letGeneralizaFon

Wheredoweintroducepolymorphicvalues?Andplacex:foralla1,...,an.sinthecontext.

let x = v ==> let x : forall a1,..,an.s = v

if v : s and a1,...,an are the variables of s

letGeneralizaFon

Wheredoweintroducepolymorphicvalues?Andplacex:foralla1,...,an.sinthecontext.Whereandhowdoweuseapolymorphicvalue?

let x = v ==> let x : forall a1,..,an.s = v

G |- x ==> x : s[b1/a1,...,bn/an), {}

if v : s and a1,...,an are the variables of s

when G(x) = forall a1,...,an.s and b1,...,bn are fresh

Whatisthecostoftypeinference?

InpracFce?LinearinthesizeoftheprogramIntheory,DEXPTIME-complete.Why?BecausewecangenerateaprogramthathasatypethatisexponenFallylarge:

let f1 x = x f1 : a -> a let f2 = f1 f1 f2 : (a -> a) -> (a -> a) let f3 = f2 f2 f3 : ((a -> a) -> (a -> a)) ->

((a -> a) -> (a -> a)) let f4 = f3 f3 ...

Summary:TypeInferenceGivenacontextG,anduntypedtermu:

–  Finde,t,qsuchthatG|-u==>e:t,q

–  FindprincipalsoluFonSofqviaunificaFon•  ifnosoluFonexists,thereisnoreconstrucFon

–  ApplyStoe,ieoursoluFonisS(e)•  S(e)containsschemaFctypevariablesa,b,c,etcthatmaybeinstanFatedwithanytype

–  SinceSisprincipal,S(e)characterizesallreconstrucFons.

–  Ifdesired,usethetypecheckingalgorithmtovalidate

top related