我们可以f :: a -> b为不同的a和实现一个多态函数b。我们怎样才能使
twice :: (a -> b) -> a -> c
twice f x = f (f x)
Run Code Online (Sandbox Code Playgroud)
类型检查?即如何编写一个应用多态函数两次的函数?
我们Rank2Types可以更接近一些,但还不够:
{-# LANGUAGE Rank2Types #-}
twice1 :: (forall a. a -> (m a)) -> b -> (m (m b))
twice1 f = f . f
twice2 :: (forall a. m a -> a) -> m (m b) -> b
twice2 f = f . f
Run Code Online (Sandbox Code Playgroud)
所以一些多态函数可以应用两次:
\> twice1 (:[]) 1
[[1]]
\> twice2 head [[1]]
1
Run Code Online (Sandbox Code Playgroud)
我们可以更进一步吗?
这个问题是10 年前在 Haskell Cafe 上提出的,但没有得到完全解答(对于类型类,它变成了很多样板文件)。
{-# LANGUAGE TypeFamilies, RankNTypes, UnicodeSyntax #-}\n\ntype family Fundep a :: *\n\ntype instance Fundep Bool = Int\ntype instance Fundep Int = String\n...\n\ntwice :: \xe2\x88\x80 a . (\xe2\x88\x80 c . c -> Fundep c) -> a -> Fundep (Fundep a)\ntwice f = f . f\nRun Code Online (Sandbox Code Playgroud)\n\n现在,这实际上没有多大用处,因为您无法定义适用于任何 c. 一种可能性是加入类约束,例如
class Showy a where\n type Fundep a :: *\n showish :: a -> Fundep a\n\ninstance Showy Bool where\n type Fundep Bool = Int\n showish = fromEnum\ninstance Showy Int where\n type Fundep Int = String\n showish = show\n\ntwice :: \xe2\x88\x80 a b . (Showy a, b ~ Fundep a, Showy b) =>\n (\xe2\x88\x80 c . Showy c => c -> Fundep c) -> a -> Fundep b\ntwice f = f . f\n\nmain = print $ twice showish False\nRun Code Online (Sandbox Code Playgroud)\n