Data.Function包含一个函数on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
,它对于一元函数类似于(.) :: (b -> c) -> (a -> b) -> a -> c
,所以我尝试编写一个函数on' :: Int -> ...
作为泛化,所以我可以写on' 1 length negate
,on' 2 length compare
等,但是这样的函数不会进行类型检查,因为on'
的第三个参数的函数结果的类型取决于第一个论点。
我怎样才能编写这样的功能?也许我必须将函数包装在自定义数据类型中,以便组合函数的类型仅取决于第一个参数的类型和最终结果的类型?
答案 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
,这使它失败。
我也试图使家庭成为单独的,但却失败了。