在这个答案中,我将稍微扩展一下我在评论中提出的建议之一。
作用于元组时是否可以看到f <$>的类型?
(<$>) 是一个多态函数:
GHCi> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
使用 GHC 8,您可以使用 TypeApplications 扩展来专门化多态函数,方法是提供其部分或全部类型变量的实例化(在本例中,f、a 和 b,按顺序排列):
GHCi> :set -XTypeApplications
GHCi> :t (<$>) @Maybe
(<$>) @Maybe :: (a -> b) -> Maybe a -> Maybe b
GHCi> :t (<$>) @Maybe @Int
(<$>) @Maybe @Int :: (Int -> b) -> Maybe Int -> Maybe b
GHCi> :t (<$>) @Maybe @_ @Bool
(<$>) @Maybe @_ @Bool :: (t -> Bool) -> Maybe t -> Maybe Bool
GHCi> :t (<$>) @_ @Int @Bool
(<$>) @_ @Int @Bool
:: Functor t => (Int -> Bool) -> t Int -> t Bool
GHCi> :t (<$>) @Maybe @Int @Bool
(<$>) @Maybe @Int @Bool :: (Int -> Bool) -> Maybe Int -> Maybe Bool
要将其与对一起使用,请使用对类型构造函数的前缀语法:
GHCi> :t (<$>) @((,) _)
(<$>) @((,) _) :: (a -> b) -> (t, a) -> (t, b)
GHCi> -- You can use the specialised function normally.
GHCi> -- That includes passing arguments to it.
GHCi> f x = x + 1
GHCi> :t (<$>) @((,) _) f
(<$>) @((,) _) f :: Num b => (t, b) -> (t, b)
((,) _) 中的 _ 未指定该对的第一个元素的类型(这是 (,) 类型构造函数的第一个参数)应该是什么。它的每一个选择都会产生不同的Functor。如果您愿意,可以更具体:
GHCi> :t (<$>) @((,) String) f
(<$>) @((,) String) f :: Num b => (String, b) -> (String, b)
最后,值得看看如果您尝试使用 3 元组会发生什么:
GHCi> :t (<$>) @((,,) _ _) f
(<$>) @((,,) _ _) f
:: (Num b, Functor ((,,) t t1)) => (t, t1, b) -> (t, t1, b)
正如 Daniel Wagner 在 his answer 中所讨论的,base 没有为 3 元组定义 Functor 实例。尽管如此,类型检查器不能排除某个地方的某个人可能已经为前两个类型参数的某些选择定义了一个特定的实例的可能性,但那将毫无意义。出于这个原因,推测性约束Functor ((,,) t t1) 出现在类型中(因为在 base 中有一个 Functor ((,) a) 实例,所以对不会发生这种情况)。正如预期的那样,一旦我们尝试实例化前两个类型参数,它就会爆炸:
GHCi> :t (<$>) @((,,) Bool String) f
<interactive>:1:1: error:
• Could not deduce (Functor ((,,) Bool String))
arising from a use of ‘<$>’
from the context: Num b
bound by the inferred type of
it :: Num b => (Bool, String, b) -> (Bool, String, b)
at <interactive>:1:1
• In the expression: (<$>) @((,,) Bool String) f