Data.Function的`on`函数如何被推广以使用n-ary函数?

时间:2017-03-25 08:11:13

标签: haskell

基础包中的

Data.Function包含一个函数on :: (b -> b -> c) -> (a -> b) -> a -> a -> c,它对于一元函数类似于(.) :: (b -> c) -> (a -> b) -> a -> c,所以我尝试编写一个函数on' :: Int -> ...作为泛化,所以我可以写on' 1 length negateon' 2 length compare等,但是这样的函数不会进行类型检查,因为on'的第三个参数的函数结果的类型取决于第一个论点。

我怎样才能编写这样的功能?也许我必须将函数包装在自定义数据类型中,以便组合函数的类型仅取决于第一个参数的类型和最终结果的类型?

1 个答案:

答案 0 :(得分:2)

这是一种可能的方法。我们首先定义类型级自然。

{-# LANGUAGE ScopedTypeVariables, TypeFamilies, DataKinds, TypeApplications, 
             AllowAmbiguousTypes, MultiParamTypeClasses, FlexibleInstances #-}
{-# OPTIONS -Wall #-}

data Nat = O | S Nat

我们使用a -> a -> ... a -> b参数定义n

type family F (n :: Nat) a b where
   F 'O a b = b
   F ('S n) a b = a -> F n a b

然后我们为on引入一个针对这些自然的自定义类,并以归纳方式为每个natiral实现它。

class On (n :: Nat) c where
   on :: forall a b. F n b c -> (a -> b) -> F n a c

instance On 'O c where
   on f _g = f

instance On n c => On ('S n) c where
   on f g = \aVal -> on @n @c (f (g aVal)) g

最后,一些例子。

fun2 :: String -> String -> String
fun2 x y = "(" ++ x ++ ", " ++ y ++ ")" 

fun3 :: String -> String -> String -> String
fun3 x y z = "(" ++ x ++ ", " ++ y ++ ", " ++ z ++ ")" 

funG :: Int -> String
funG n = replicate n '#'

test2 :: String
test2 = on @('S ('S 'O)) fun2 funG 1 2

test3 :: String
test3 = on @('S ('S ('S 'O))) fun3 funG 1 2 3

相对偏离主题的说明:

我找不到从类型类中删除c参数的方法。由于c不是从类型中确定的,因此它是不明确的,因此我必须明确地传递它(通过类型应用 - 如上所述 - 或Proxy)。但是,为了通过它,我需要c在范围内。如果我从课程中删除c,它就会超出范围。如果我使用实例签名,我可以将c带回范围,但由于类型歧义,GHC不会将其识别为相同的c

OnGeneralization2.hs:18:10: error:
    • Couldn't match type ‘F n a c0’ with ‘F n a c’
      Expected type: F ('S n) b c -> (a -> b) -> F ('S n) a c
        Actual type: F ('S n) b c0 -> (a -> b) -> F ('S n) a c0
      NB: ‘F’ is a type function, and may not be injective
      The type variable ‘c0’ is ambiguous
    • When checking that:
          forall a b c. F ('S n) b c -> (a -> b) -> F ('S n) a c
        is more polymorphic than:
          forall a b c. F ('S n) b c -> (a -> b) -> F ('S n) a c
      When checking that instance signature for ‘on’
        is more general than its signature in the class
        Instance sig: forall a b c.
                      F ('S n) b c -> (a -> b) -> F ('S n) a c
           Class sig: forall a b c.
                      F ('S n) b c -> (a -> b) -> F ('S n) a c
      In the instance declaration for ‘On ('S n)’

注意最后一行:它们是完全相同的类型,但是为了检查它们的子类型,GHC仍然使用新的Skolem类型常量c0,这使它失败。

我也试图使家庭成为单独的,但却失败了。