有人在练习有关将函数两次应用于某项函数的练习时问我,我认为这很有趣。
我们的想法是,我们应该使函数两次,这需要一个函数和一个输入,然后两次应用该函数,例如
twice :: (a -> a) -> a -> a
twice f x = f ( f x )
通常来说,打字是很有意义的。
不幸的是,对于元组中的元组和函数fst
,我们可能认为可以在((1,2),3)
上使用它,但是由于twice
的类型,这是不可能的。
有没有办法使类似的东西起作用?
答案 0 :(得分:7)
由于您的f = fst
是多态的,并且两个调用隐式涉及不同的类型,因此无法键入。如果我们使呼叫明确,则它们将变为:
fst @ (Int,Int) (fst @ ((Int,Int),Int) ((1,2),3))
可能为twice
使用不同的类型,要求参数必须是多态函数。这需要Rank2Types
:
twice' :: (forall a b . (a, b) -> a) -> ((a,b),c) -> a
twice' f x = f ( f x )
但是,上面的函数用途有限,因为f
的唯一有意义的选择是fst
-没有其他(forall a b . (a, b) -> a)
类型的终止函数。
应该也可以使用类型类,并打开一些扩展名。
class C a where
type Res a
theF :: a -> Res a
instance C (a, b) where
type Res (a, b) = a
theF = fst
twiceC :: (C a, C (Res a)) => a -> Res (Res a)
twiceC x = theF (theF x)
但是,此函数必须在instance
中定义,而不是作为参数传递。