我发现,最简单的方式是使用typed holes提供GHCI 7.8:
> (*10) <$> _a $ 1
Found hole ‘_a’ with type: s0 -> b
Where: ‘s0’ is an ambiguous type variable
‘b’ is a rigid type variable bound by
the inferred type of it :: b at <interactive>:4:1
Relevant bindings include it :: b (bound at <interactive>:4:1)
In the second argument of ‘(<$>)’, namely ‘_a’
In the expression: (* 10) <$> _a
In the expression: (* 10) <$> _a $ 1
所以这告诉我,a :: s0 -> b
。其次是要弄清楚运营商的顺序:
> :i (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
infixl 4 <$>
> :i ($)
($) :: (a -> b) -> a -> b
infixr 0 $
因此,这说$
高度右结合,并赋予它的类型,我们看到它的第一个参数必须是一个函数,所以a
必须是一个函数(双重确认)。这意味着(*10) <$> a $ 1
与((*10) <$> a) $ 1
相同,所以我们首先关注(*10) <$> a
。
> :t ((*10) <$>)
((*10) <$>) :: (Num a, Functor f) => f a -> f a
> :t (<$> _a)
Found hole ‘_a’ with type: f a
Where: ‘a’ is a rigid type variable bound by
the inferred type of it :: (a -> b) -> f b at Top level
‘f’ is a rigid type variable bound by
the inferred type of it :: (a -> b) -> f b at Top level
In the second argument of ‘(<$>)’, namely ‘_a’
In the expression: (<$> _a)
所以我们需要a
是一个仿函数。什么是可用的实例?
> :i Functor
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
-- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘Data.Maybe’
instance Functor (Either a) -- Defined in ‘Data.Either’
instance Functor ZipList -- Defined in ‘Control.Applicative’
instance Monad m => Functor (WrappedMonad m)
-- Defined in ‘Control.Applicative’
instance Control.Arrow.Arrow a => Functor (WrappedArrow a b)
-- Defined in ‘Control.Applicative’
instance Functor (Const m) -- Defined in ‘Control.Applicative’
instance Functor [] -- Defined in ‘GHC.Base’
instance Functor IO -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((,) a) -- Defined in ‘GHC.Base’
所以(->) r
恰好是一个,这是真棒,因为我们知道a
必须是一个函数。从Num
约束中,我们可以确定r
必须与Num a => a
相同。这意味着(*10) <$> a :: Num a => a -> a
。从那我们然后申请1
它,我们会得到(*10) <$> a $ 1 :: Num a
,其中a
是一些未知的功能。
所有这一切都可以使用GHCi使用:t
和:i
,以及打字孔。当然,涉及的步骤相当多,但是当您试图分解复杂表达式时,它绝不会失败,只需查看不同子表达式的类型即可。