Embedding non-positional keyword arguments in Haskell

I am trying to implement keyword arguments in Haskell similar to those 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 (to get a new function that takes the remaining keyword arguments).

I tried to implement this using DataKinds and a class type that represents a "value that can be converted to a function a -> b". The idea is that a function that takes two arguments of the keyword fooand barcan 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 if foothey barhave different types (and is xnot the function itself that accepts fooor bar), which I tried to achieve with KwargGADT.

Functional dependency in the class Funwas supposed to make the connection between f, aand bmore explicit, but I'm not sure if this really helped. I get a compiler error with and without it.

I have included the following language extensions:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}

Content 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

Content 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 I get when creating 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 compile this? Is this general approach reasonable and why do I need to better understand to make it work?

Thank!

Conclusion ghc --version:

The Glorious Glasgow Haskell Compilation System, version 8.0.2
+4
source share
1 answer

The problem is that your instances overlap:

instance (Fun f' a' b) => Fun (a -> f') a' (a -> b) where

instance Fun (Kwarg name t -> b) (Kwarg name t) b
  -- this is actually a special case of the above, with a ~ a' ~ KWarg name t

It becomes clearer if we replace the functional dependence (which is IMO, as a rule, it’s a little difficult to reason) with an equivalent type family:

{-# LANGUAGE TypeFamilies #-}

class Fun f a where
  type FRes f a :: *
  runFun :: f -> a -> FRes f a

instance Fun f' a' => Fun (a -> f') a' where
  type FRes (a -> f') a' = a -> FRes f' a'
  runFun f a' = \a -> runFun (f a) a'

instance Fun (Kwarg name t -> b) (Kwarg name t) where
  type FRes (Kwarg name t -> b) (Kwarg name t) = b
  runFun = id

In this case, the compiler message is pretty clear:

    Conflicting family instance declarations:
      FRes (a -> f') a' = a -> FRes f' a'
        -- Defined at /tmp/wtmpf-file10498.hs:20:8
      FRes (Kwarg name t -> b) (Kwarg name t) = b
        -- Defined at /tmp/wtmpf-file10498.hs:24:8
   |
20 |   type FRes (a -> f') a' = a -> FRes f' a'
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

- , . , . , (, FunDeps ) :

type family FRes f a where
  FRes (Kwarg name t -> b) (Kwarg name t) = b
  FRes (a -> f') a' = a -> FRes f' a'

runFun, - , GHC:

class Fun f a where
  runFun :: f -> a -> FRes f a

instance {-# OVERLAPPABLE #-} (Fun f' a', FRes (a -> f') a' ~ (a->FRes f' a'))
               => Fun (a -> f') a' where
  runFun f a' = \a -> runFun (f a) a'

instance {-# OVERLAPS #-} Fun (Kwarg name t -> b) (Kwarg name t) where
  runFun = id

, .

, , : .

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) )

/tmp/wtmpf-file10498.hs:34:5: error:
    • Couldn't match expected type ‘Kwarg "some" Int -> b0’
                  with actual type ‘FRes
                                      (Kwarg "test" a0 -> Kwarg "some" a0 -> a0) (Kwarg "test" Int)’
      The type variables ‘a0’, ‘b0’ are ambiguous
    • The function ‘runFun’ is applied to three arguments,
      but its type ‘(Kwarg "test" a0 -> Kwarg "some" a0 -> a0)
                    -> Kwarg "test" Int
                    -> FRes
                         (Kwarg "test" a0 -> Kwarg "some" a0 -> a0) (Kwarg "test" Int)’
      has only two
      In the expression:
        runFun
          test (Value 5 :: Kwarg "test" Int) (Value 6 :: Kwarg "some" Int)
      In the 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) )
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

, : . , Haskell , . , -

type family FFun a b where

. , . , , , , ; , - , , ​​ (, , ).

+1

Source: https://habr.com/ru/post/1682450/


All Articles