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

Popular posts from this blog

php - Vagrant up error - Uncaught Reflection Exception: Class DOMDocument does not exist -

vue.js - Create hooks for automated testing -

Add new key value to json node in java -