haskell - Polymorphism: a constant versus a function -


i'm new haskell , come across puzzling example me in haskell programming first principles book. @ end of chapter 6 occurred me following doesn't work:

constant :: (num a) => constant = 1.0 

however, following works fine:

f :: (num a) => -> f x = 3*x 

i can input numerical value x function f , nothing break. it's not constrained taking integers. makes sense me intuitively. example constant totally confusing me.

over on reddit thread book explained (paraphrasing) reason why constant example doesn't work type declaration forces value of constant things aren't more specific num. trying assign value subclass of num fractional isn't kosher.

if explanation correct, wrong in thinking these 2 examples seem opposites of each other? in 1 case, type declaration forces value general possible. in other case, accepted values function can anything implements num.

can set me straight on this?

it can read types game played between 2 actors, implementor of type , user of type. job of explaining perspective, have introduce haskell hides default: add binders type variables. types become:

constant :: forall a. num => f :: forall a. num => -> 

now, read type formation rules thusly:

  • forall a. t means: caller chooses type a, , game continues t
  • c => t means: caller shows constraint c holds, , game continues t
  • t -> t' means: caller chooses value of type t, , game continues t'
  • t (where t monomorphic type such bare variable or integer or similar) means: implementor produces value of type a

we need few other details understand things here, them here:

  • when write number no decimal points, compiler implicitly converts call frominteger applied integer produced parsing number. have frominteger :: forall a. num => integer -> a.
  • when write number decimal points, compiler implicitly converts call fromrational applied rational produced parsing number. have fromrational :: forall a. fractional => rational -> a.
  • the num class includes method (*) :: forall a. num => -> -> a.

now let's try walk through 2 examples , carefully.

constant :: forall a. num => constant = 1.0 {- = fromrational (1 % 1) -} 

the type of constant says: caller chooses type, shows type implements num, , implementor must produce value of type. implementor tries play own game calling fromrational :: fractional => rational -> a. chooses same type caller did, , makes attempt show type implements fractional. oops! can't show that, because thing caller proved him a implements num -- doesn't guarantee a implements fractional. dang. implementor of constant isn't allowed call fromrational @ type.

now, let's @ f:

f :: forall a. num => -> f x = 3*x {- = frominteger 3 * x -} 

the type of f says: caller chooses type, shows type implements num, , chooses value of type. implementor must produce value of type. going playing own game (*) , frominteger. in particular, chooses same type caller did. frominteger , (*) demand prove type instance of num -- passes off proof caller gave him of , saves day! chooses integer 3 argument frominteger, , chooses result of , value caller handed him 2 arguments (*). satisfied, , implementor gets return new value.

the point of whole exposition this: num constraint in both cases enforcing exactly same thing, namely, whatever type choose instantiate a @ must member of num class. it's in definition constant = 1.0 being in num isn't enough operations we've written, whereas in f x = 3*x being in num is enough operations we've written. , since operations we've chosen 2 things different, should not too surprising 1 works , other doesn't!


Comments