Haskell:应用多态函数两次

beh*_*uri 5 haskell

我们可以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 上提出的,但没有得到完全解答(对于类型类,它变成了很多样板文件)。

lef*_*out 4

{-# 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\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,这实际上没有多大用处,因为您无法定义适用于任何 c. 一种可能性是加入类约束,例如

\n\n
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\n
Run Code Online (Sandbox Code Playgroud)\n