I am trying to implement keyword arguments in Haskell, similar to the ones found in Ocaml. My goal is to have arguments that can be passed in any order, and can be partially applied in a function call (yielding a new function that takes the remaining keyword arguments).
I've tried to implement this using DataKinds and a type-class that represents "a value that can be converted into a function a -> b." The idea is that a function taking two keyword arguments, foo and bar, can be converted either to a function that looks like foo -> bar -> x or to a function that looks like bar -> foo -> x. This should be unambiguous so long as foo and bar have different types (and x isn't itself a function that takes foo or bar), which is what I've tried to achieve with the Kwarg GADT.
The functional dependency in the Fun class was meant to make the relationship between f, a, and b more explicit, but I'm not sure whether this actually helped. I get a compiler error with and without it.
I have enabled the following language extensions:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
Contents of Quoter.hs:
module Quoter where
import GHC.TypeLits (Symbol)
data Kwarg :: Symbol -> * -> * where
Value :: a -> Kwarg b a
class Fun f a b | f a -> b where
runFun :: f -> a -> b
instance (Fun f' a' b) => Fun (a -> f') a' (a -> b) where
runFun f a' = \a -> runFun (f a) a'
instance Fun (Kwarg name t -> b) (Kwarg name t) b where
runFun = id
Contents of Main.hs:
module Main where
import Quoter
test :: (Num a) => Kwarg "test" a -> Kwarg "some" a -> a
test (Value i) (Value j) = i + j
main = putStrLn $ show $
runFun test (Value 5 :: Kwarg "some" Int) (Value 6 :: Kwarg "test" Int)
This is the error that I get when building with ghc:
Main.hs:18:3: error:
• Couldn't match type ‘Kwarg "some" Int -> b’ with ‘Int’
arising from a functional dependency between:
constraint ‘Fun (Kwarg "some" Int -> Int) (Kwarg "some" Int) Int’
arising from a use of ‘runFun’
instance ‘Fun (a -> f') a' (a -> b1)’ at <no location info>
• In the second argument of ‘($)’, namely
‘runFun
test (Value 5 :: Kwarg "some" Int) (Value 6 :: Kwarg "test" Int)’
In the second argument of ‘($)’, namely
‘show
$ runFun
test (Value 5 :: Kwarg "some" Int) (Value 6 :: Kwarg "test" Int)’
In the expression:
putStrLn
$ show
$ runFun
test (Value 5 :: Kwarg "some" Int) (Value 6 :: Kwarg "test" Int)
Do you know what I need to change in order to get this to compile? Is this general approach sensible, and what do I need to understand better in order to get this to work?
Thanks!
Output of ghc --version:
The Glorious Glasgow Haskell Compilation System, version 8.0.2