我认为@HTNW 的答案可能涵盖了它,但为了完整起见,以下是inContext
解决方案的详细工作原理。
函数的类型签名:
inContext :: a -> (a -> b) -> a
这意味着,如果你有一个你想要输入的东西,以及一个使用它的“上下文”(可以表达为一个将它作为参数的 lambda),那么用类型说:
thing :: a1
context :: a2 -> b
您可以通过构造表达式来强制统一a1
(的一般类型thing
)和(上下文的约束):a2
thing `inContext` context
通常,统一的类型thing :: a
会丢失,但是 的类型签名inContext
意味着整个结果表达式的类型也将与所需的类型统一a
,GHCi 会很高兴地告诉您该表达式的类型。
所以表达式:
(.) `inContext` \hole -> hole digitToInt
最终被分配(.)
在指定上下文中的类型。你可以这样写,有点误导,如下:
(.) `inContext` \(.) -> (.) digitToInt
因为(.)
是匿名 lambda 的参数名称hole
。这可能会令人困惑,因为我们正在创建一个隐藏 的顶级定义的本地绑定(.)
,但它仍然命名相同的东西(使用精炼的类型),并且这种对 lambdas 的滥用允许我们(.) digitToInt
逐字编写原始表达式,使用适当的样板。
inContext
如果您只是向 GHCi 询问它的类型,那么它实际上是如何定义的,那么它inContext = undefined
会起作用。但是,只要看一下类型签名,就很容易给出inContext
一个有效的定义:
inContext :: a -> (a -> b) -> a
inContext a _ = a
事实证明,这只是 的定义const
,所以inContext = const
也有效。
您可以使用inContext
一次键入多个内容,它们可以是表达式而不是名称。为了适应前者,您可以使用元组;为了使后者起作用,您在 lambas 中使用了更合理的参数名称。
因此,例如:
λ> :t (fromJust, fmap length) `inContext` \(a,b) -> a . b
(fromJust, fmap length) `inContext` \(a,b) -> a . b
:: Foldable t => (Maybe Int -> Int, Maybe (t a) -> Maybe Int)
告诉您,在表达式fromJust . fmap length
中,类型已专门用于:
fromJust :: Maybe Int -> Int
fmap length :: Foldable t => Maybe (t a) -> Maybe Int