haskell - Type inference of instance functions -
the problem
i want able create 2 data types
: a
, b
, create 2 functions f
:
f :: -> int -> int
f :: b -> string -> string -> string
the way can (so far know) use type classes
, instances
.
the problem is, not want explicit write f
signatures - want type checker infer me. possible?
example code
{-# language flexibleinstances, functionaldependencies, undecidableinstances #-} data = a{ax::int} deriving(show) data b = b{bx::int} deriving(show) data c = c{cx::int} deriving(show) -- don't want explicit signature int->int -- love write: -- instance func_f (a->b) instance func_f (int->int) f _ = i*2 -- don't want explicit signature string->string->string -- love write: -- instance func_f b (a->b->c) instance func_f b (string->string->string) f _ s1 s2 = "test"++s1++s2 -- don't want explicit signature a->a -- love write: -- instance func_f c (a->b) instance func_f c (a->a) f _ = class func_f b | -> b f :: -> b f2 _ s1 s2 = "test"++s1++s2 -- here type inferencer automaticly recognizes signature main :: io () main = let = 1 b = b 2 c = c 3 a_out = f 5 b_out = f b "a" "b" c_out = c 6 print a_out print b_out print c_out
explaination
i'm writing custom domain language compiler , i'm generating haskell code result. don't want final users of language write explicit types, want use haskells powerful type system infer as possible.
if write function f2 _ s1 s2 = "test"++s1++s2
not have explicit write signature - because compiler can infer it. can somehow ask compiler infer signatures of f
in above example?
i love know every possible "hack" solve problem, if hack "ugly", because i'm generating haskell code , not have "pretty".
here's 1 way kind of works. there many more cases cover if fa fb have type variables in inferred types. in case following code have pattern match failures @ compile time.
{-# language flexibleinstances, functionaldependencies, templatehaskell #-} import language.haskell.th data = a{ax::int} deriving(show) data b = b{bx::int} deriving(show) fa a{} = i*(2 :: int) fb b{} s1 s2 = "test"++s1++s2 class func_f b | -> b f :: -> b let getletter (appt (appt _ x) _) = x getsnd (appt x y) = y mkinst name0 = (vari n ty _ _) <- reify name0 fmap (:[]) $ instanced (return []) [t| func_f $(return $ getletter ty) $(return $ getsnd ty) |] [vald (varp 'f) (normalb (vare name0)) []] in fmap concat $ mapm mkinst ['fb, 'fa] main :: io () main = let = 1 b = b 2 a_out = f 5 b_out = f b "a" "b" print a_out print b_out
whether helps language you're compiling haskell question.
added hints
if type polymorphic, you'll see reify giving isn't covered example code above.
> :set -xtemplatehaskell > :m +ipprint language.haskell.th > putstrln $(reify 'id >>= stringe . pshow)
prints out describes (a->a):
vari ghc.base.id (forallt [plaintv a_1627394484] [] (appt (appt arrowt (vart a_1627394484)) (vart a_1627394484))) nothing (fixity 9 infixl)
with bit of work, split type in there cxtq , typeq instanced needs.
i don't know how sense makes generate (a->b) instead. go replacing type variables new unique ones, best done data.generics.everywherem because data type has many many constructors.
you'll still have issue invalid:
instance func_f (a -> b) f _ = id
this approach getting (a->b) can make program crash:
instance func_f (a -> b) f _ = unsafecoerce id
this approach lets instance chosen when types different, @ later point in ghc's execution can end failure if 'a' , 'b' can't same.
instance (a~b) => func_f (a->b) f _ = unsafecoerce id
Comments
Post a Comment