簡體   English   中英

學習Haskell - 如何簡化表達式?

[英]Learning Haskell - How to simplify expressions?

有沒有辦法從表達簡化中解脫出來?

例如,給出這個表達式:

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

我很想看到一個可以解釋它意味着什么的工具。 對於初學者來說(在源中找到正確的實例函數定義,檢查運算符優先級)來簡化表達式並包含所有相關步驟,這是非常費力的:

fmap (+) a <*> b $ 1

請參閱Data.Functor 定義

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

參見Control.Monad.Instances fmapinstance Functor ((->) r)

等等。

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

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

我發現簡單的方法是使用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 ,以及打字孔來發現。 當然,涉及到相當多的步驟,但是當你試圖分解復雜的表達式時,它永遠不會失敗,只需看看不同子表達式的類型。

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

我也想建議Hoogle ,因為與即時搜索啟用(在正確的頂側欄有它的按鈕),你可以搜索功能非常迅速,它可以提供更多, 更多的信息比GHCI,最好的部分是你不必提及要在其中搜索的模塊1 這與您必須首先導入的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 hooglecabal install hoogle )並從命令行執行查詢( hoogle your-query )。
旁注:您必須先運行hoogle data才能收集信息。 它需要wget / curl ,所以如果你在Windows上,你可能需要首先在你的路徑中獲取 (或者當然是Windows的卷曲)。 在Linux上,它幾乎總是內置的(如果你沒有在Linux上,只需要apt-get它)。 順便說一下,我從不使用命令行中的Hoogle,它根本不是可訪問的,但它仍然非常有用,因為一些文本編輯器及其插件可以利用它。

或者你可以使用FPComplete的Hoogle ,這有時會更令人滿意(因為根據我的經驗,它已經知道更多第三方庫。我只在那些“Hoogling會話”中使用它)。

還有Hayoo! 順便說說。

1在Hoogle中,您可能> 95%的時間不必執行此操作,但是如果由於某種原因未對其進行搜索,則可以使用+Module來導入模塊(對於第三方庫來說有時會這樣)。
您也可以通過-Module過濾掉模塊。
例如: destroyTheWorld +World.Destroyer -World.Destroyer.Mercy找到destroyTheWorld ,並確保你沒有看到仁慈的方式去做(這對於不同版本具有相同功能名稱的模塊來說非常方便,比如Data.ByteStringData.ByteString.LazyData.VectorData.Vector.Mutable等中的那些。

哦和Hoogle的另一個好處是,它不僅可以顯示功能的簽名,還可以帶您進入模塊的Haddock頁面,因此您還可以在這些頁面中獲取文檔+,如果可用,您可以單擊“源”在每個功能的右側,查看如何實現更多信息。

這超出了問題的范圍,但是Hoogle也用於查詢功能簽名,這只是...非常有幫助。 如果我想要一個帶索引號和列表的函數並給我該索引中的元素,我想知道它是否已經內置,我可以在幾秒鍾內搜索它。
我知道函數需要一個數字和一個列表,並給我一個列表的元素,所以函數簽名必須看起來沿着這些行: Int -> [a] -> a (或一般來說: Num a => a -> [b] -> b ),兩個實例都顯示實際上有一個函數( (!!)genericIndex )。

GHCi的優勢在於你可以玩表達,探索它們等等。很多時候處理抽象函數意味着很多。
能夠:l (oad)非常非常有幫助。

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

啟動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中的命令。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM