2014-08-28 93 views
10

有沒有什麼辦法可以消除表情簡化帶來的痛苦?學習Haskell - 如何簡化表達式?

例如,假設這個表達式:

(+) <$> a <*> b $ 1 

我很想看到一個工具,可以解釋這是什麼意思。這對於初學者相當費力(發現在來源正確的實例函數定義,檢查運算符優先級),以簡化與所涉及的所有步驟的表達式:

fmap (+) a <*> b $ 1 

definitionData.Functor

(.) (+) a <*> b $ 1 

fmapControl.Monad.Instancesinstance Functor ((->) r)

等等。

編輯:爲了澄清,我正在尋找一種使用實際函數定義重寫表達式的方法,以便新手能夠理解此表達式的結果。如何辨別(<$>) = fmap?我不知道如何使用hoogle和其他工具找到特定的實例定義(源代碼)。

編輯:更改不正確的原始表達式以匹配下面的減少。

回答

3

開始ghci中,:cd你正在閱讀的來源,:load模塊你有興趣,並使用:i命令來獲取信息的基本目錄:

ghci> :i <$> 
(<$>) :: Functor f => (a -> b) -> f a -> f b 
    -- Defined in `Data.Functor' 
infixl 4 <$> 
ghci> :i $ 
($) :: (a -> b) -> a -> b -- Defined in `GHC.Base' 
infixr 0 $ 
ghci> :i . 
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base' 
infixr 9 . 

告訴你的類型,它定義的位置,關聯性(infixlinfixr)和優先級(數字;越高越嚴格)。所以(*10) <$> a $ 1被讀作((*10) <$> a) $ 1

當您使用:load模塊時,該模塊內範圍內的所有名稱將在ghci中的範圍內。如果你在代碼中有錯誤,那麼你不能在:i裏面找到任何東西。在這些情況下,您可以對線條進行註釋,使用undefined,並且可能還會使用behlkir建議的打孔(尚未與那些打針太多)。

當你在它的時候,嘗試在ghci中的:?命令。

6

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

5

GHCi是非常正確的建議,我也建議。

我也想建議Hoogle,因爲與即時搜索啓用(在正確的頂側欄有它的按鈕),你可以搜索功能非常迅速,它可以提供很多,比GHCi更多的信息,最好的部分是你不必提及在其中搜索模塊。這與您必須先導入的GHCi相反:

ghci> :t pure 
<interactive>:1:1: Not in scope: ‘pure’ 
ghci> :m +Control.Applicative 
ghci> :t pure 
pure :: Applicative f => a -> f a 

上面的Hoogle鏈接只是一個(來自Haskell.org站點)。 Hoogle是一個程序,您也可以在您的機器上安裝(cabal install hoogle)並從命令行(hoogle your-query)執行查詢。
旁註:您必須首先運行hoogle data來收集信息。它需要wget/curl,所以如果你在Windows上,你可能首先需要在你的路徑中獲得this(或者當然是用於Windows的curl)。在Linux上它幾乎總是內置的(如果你沒有在Linux上使用它,只需要apt-get它)。順便說一下,我從來沒有在命令行中使用Hoogle,但它不是那麼容易訪問,但它仍然非常有用,因爲一些文本編輯器及其插件可以利用它。

或者您可以使用FPComplete's Hoogle這有時是更令人滿意的(因爲在我的經驗,它已經意識到更多的第三方庫。我只在那些「Hoogling會議」使用它)。

另外還有Hayoo!

在Hoogle你可能>的95%的時間不會有這樣做,但+Module進口,如果由於某種原因,它沒有被搜索(這是這種情況有時是第三方庫)的模塊。
您還可以通過-Module過濾模塊。
例如:destroyTheWorld +World.Destroyer -World.Destroyer.Mercy找到destroyTheWorld,並確保你不看仁慈的方式做到這一點(這都非常方便與不同版本的函數的名稱,像那些模塊Data.ByteString & Data.ByteString.LazyData.Vector & Data.Vector.Mutable等)。

哦,Hoogle的另一個驚人的優勢是它不僅可以顯示函數的簽名,還可以將您帶到模塊的Haddock頁面,因此您還可以在這些頁面獲得文檔+,可以點擊在每個函數右側的「源代碼」上查看它是如何實現以獲取更多信息的。

這超出了問題的範圍,但Hoogle也用於查詢功能簽名,這只是.. mindblowingly有幫助。如果我想要一個帶有索引號和列表的函數,並給出該索引中的元素,並且我想知道它是否已經內置,我可以在幾秒鐘內搜索它。
我知道函數接受一個數字和一個列表,並給我的列表的元素,因此函數簽名必須看東西沿着這些路線:Int -> [a] -> a(或一般:Num a => a -> [b] -> b),並且這兩種情況下出現,真的有一個函數((!!)genericIndex)。

凡GHCI有upperhand是抽象的功能,這意味着大量的交易時,你可以玩弄的表達,探索它們,等等很多倍。
能夠:l(oad)非常有幫助。

如果您只是在尋找功能簽名,您可以結合Hoogle和GHCi。
在GHCI你可以輸入:! cmd,並GHCI將在命令行執行cmd,並打印結果。這意味着您也可以在GHCi中使用Hoogle,例如:! hoogle void