types - Implementing non-positional keyword arguments in Haskell -
i trying implement keyword arguments in haskell, similar ones found in ocaml. goal have arguments can passed in order, , can partially applied in function call (yielding new function takes remaining keyword arguments).
i've tried implement using datakinds , type-class represents "a value can converted function a -> b
." idea function taking 2 keyword arguments, foo
, bar
, can converted either function looks foo -> bar -> x
or function looks bar -> foo -> x
. should unambiguous long foo
, bar
have different types (and x
isn't function takes foo
or bar
), i've tried achieve kwarg
gadt.
the functional dependency in fun
class meant make relationship between f
, a
, , b
more explicit, i'm not sure whether helped. compiler error , without it.
i have enabled following language extensions:
{-# language datakinds #-} {-# language kindsignatures #-} {-# language gadts #-} {-# language multiparamtypeclasses #-} {-# language flexibleinstances #-} {-# language functionaldependencies #-} {-# language undecidableinstances #-}
contents of quoter.hs
:
module quoter import ghc.typelits (symbol) data kwarg :: symbol -> * -> * value :: -> kwarg b class fun f b | f -> b runfun :: f -> -> b instance (fun f' a' b) => fun (a -> f') a' (a -> b) runfun f a' = \a -> runfun (f a) a' instance fun (kwarg name t -> b) (kwarg name t) b runfun = id
contents of main.hs
:
module main import quoter test :: (num a) => kwarg "test" -> kwarg "some" -> test (value i) (value j) = + j main = putstrln $ show $ runfun test (value 5 :: kwarg "some" int) (value 6 :: kwarg "test" int)
this error when building ghc:
main.hs:18:3: error: • couldn't match type ‘kwarg "some" int -> b’ ‘int’ arising functional dependency between: constraint ‘fun (kwarg "some" int -> int) (kwarg "some" int) int’ arising use of ‘runfun’ instance ‘fun (a -> f') a' (a -> b1)’ @ <no location info> • in second argument of ‘($)’, namely ‘runfun test (value 5 :: kwarg "some" int) (value 6 :: kwarg "test" int)’ in second argument of ‘($)’, namely ‘show $ runfun test (value 5 :: kwarg "some" int) (value 6 :: kwarg "test" int)’ in expression: putstrln $ show $ runfun test (value 5 :: kwarg "some" int) (value 6 :: kwarg "test" int)
do know need change in order compile? general approach sensible, , need understand better in order work?
thanks!
output of ghc --version
:
the glorious glasgow haskell compilation system, version 8.0.2
the problem instances overlapping:
instance (fun f' a' b) => fun (a -> f') a' (a -> b) instance fun (kwarg name t -> b) (kwarg name t) b -- special case of above, ~ a' ~ kwarg name t
it becomes clearer if replace functional dependency (which imo bit hard reason about) equivalent associated type family:
{-# language typefamilies #-} class fun f type fres f :: * runfun :: f -> -> fres f instance fun f' a' => fun (a -> f') a' type fres (a -> f') a' = -> fres f' a' runfun f a' = \a -> runfun (f a) a' instance fun (kwarg name t -> b) (kwarg name t) type fres (kwarg name t -> b) (kwarg name t) = b runfun = id
in case, compiler message pretty clear:
conflicting family instance declarations: fres (a -> f') a' = -> fres f' a' -- defined @ /tmp/wtmpf-file10498.hs:20:8 fres (kwarg name t -> b) (kwarg name t) = b -- defined @ /tmp/wtmpf-file10498.hs:24:8 | 20 | type fres (a -> f') a' = -> fres f' a' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
overlapping instances big problem, in particular when nontrivial type-level functions need resolved. however, it's not problem hasn't been encountered before. solution switch instance-clause-based type-level functions (i.e. fundeps or associated type-families) closed type families:
type family fres f fres (kwarg name t -> b) (kwarg name t) = b fres (a -> f') a' = -> fres f' a'
to implement runfun
, still need class overlapping instances, these can hacked worked ghc pragmas:
class fun f runfun :: f -> -> fres f instance {-# overlappable #-} (fun f' a', fres (a -> f') a' ~ (a->fres f' a')) => fun (a -> f') a' runfun f a' = \a -> runfun (f a) a' instance {-# overlaps #-} fun (kwarg name t -> b) (kwarg name t) runfun = id
as such, test work.
unfortunately not it's supposed though: allow argument order changed.
main = putstrln $ show ( runfun test (value 5 :: kwarg "some" int) (value 6 :: kwarg "test" int) , runfun test (value 5 :: kwarg "test" int) (value 6 :: kwarg "some" int) )
gives
/tmp/wtmpf-file10498.hs:34:5: error: • couldn't match expected type ‘kwarg "some" int -> b0’ actual type ‘fres (kwarg "test" a0 -> kwarg "some" a0 -> a0) (kwarg "test" int)’ type variables ‘a0’, ‘b0’ ambiguous • function ‘runfun’ applied 3 arguments, type ‘(kwarg "test" a0 -> kwarg "some" a0 -> a0) -> kwarg "test" int -> fres (kwarg "test" a0 -> kwarg "some" a0 -> a0) (kwarg "test" int)’ has 2 in expression: runfun test (value 5 :: kwarg "test" int) (value 6 :: kwarg "some" int) in first argument of ‘show’, namely ‘(runfun test (value 5 :: kwarg "some" int) (value 6 :: kwarg "test" int), runfun test (value 5 :: kwarg "test" int) (value 6 :: kwarg "some" int))’ | 34 | , runfun test (value 5 :: kwarg "test" int) (value 6 :: kwarg "some" int) ) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
the essential problem type inference goes in less favourable direction: starting-values end-results. avaible direction in languages, in haskell it's better infer end result individual expressions, because outer result available unification environment. in end, leads
type family ffun b
i.e. type of function determined result want , argument can supply. not have overlapping instances based on whether argument @ hand happens first 1 function expects; rather you'd build sort of type-level map including keyed arguments , have function accept 1 such map (or, equivalently, arguments in alphabetic order).
Comments
Post a Comment