我發現,最簡單的方式是使用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
,以及打字孔。當然,涉及的步驟相當多,但是當您試圖分解複雜表達式時,它絕不會失敗,只需查看不同子表達式的類型即可。