簡體   English   中英

Haskell:了解ghci中的“無實例”錯誤消息

[英]Haskell : understanding “No instance for” error messages in ghci

問題1

嗨,如果我在WinGHCi中故意執行以下錯誤代碼:

3 4

然后我得到的錯誤信息是

<interactive>:1:1:
    No instance for (Num (a0 -> t0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a0 -> t0))
    In the expression: 3
    In the expression: 3 4
    In an equation for `it': it = 3 4

No instance for (Num (a0 -> t0))到底是什么意思?

問題2

為什么下面的代碼:

(+) 2 3 4
<interactive>:1:7:
    No instance for (Num (a0 -> t0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a0 -> t0))
    In the second argument of `(+)', namely `3'
    In the expression: (+) 2 3 4
    In an equation for `it': it = (+) 2 3 4

與第二段代碼產生的錯誤略有不同:

2+3 4
<interactive>:1:3:
    No instance for (Num (a1 -> a0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a1 -> a0))
    In the expression: 3
    In the second argument of `(+)', namely `3 4'
    In the expression: 2 + 3 4

即在第一段代碼中No instance for (Num (a0 -> t0)) ,而在第二段代碼中我們No instance for (Num (a1 -> a0))


[回應ehird]

(問題從答案評論移到):

1)我理解后兩個表達式是不同的,但是您是在說我不應該理解為什么解釋器為前者選擇(Num (a0 -> t0))並為(Num(a1 -> a0))選擇后者,除了事實不同之外?

2)嗨,對於前者,當您說“但是函數沒有Num實例”時,您是什么意思? 抱歉,我不清楚實例的概念是什么。 此外,出於好奇,您是否可以使用實例Num (a -> b)方法以某種方式告訴解釋器將3 4解釋為4 modulo 3

我的意圖是用更多的解釋來補充ehird的回答。 當你寫表達

3 4

然后,Haskell解釋器認為您正在嘗試將函數3應用於4 為了讓Haskell將3解釋為一個函數,需要調用該函數

fromInteger :: Integer -> (a -> b)

為了從整數3獲得函數(即類型a -> b某種東西)。 現在,在Num類型類中將fromInteger定義為具有簽名

instance Num x where
    fromInteger :: Integer -> x

例如,當將類型x設為Num類的實例時,將提供fromInteger的實現,該實現告訴Haskell如何將整數文字轉換為x 在您的情況下, x是函數類型a -> b 讓我們開始吧!


首先,一些樣板。 要使x成為Num Haskell的實例,還需要使它也成為ShowEq的實例:

instance Show (a -> b) where show f = "<function>"
instance Eq (a -> b) where f == g = False

現在假設我們要將3 4解釋為“ 4模3”。 然后,我們需要告訴Haskell如何將任何整數解釋為調用mod的函數。 此外,由於mod僅接受整數類型(它具有簽名mod :: Integral a => a -> a -> a ),因此我們還需要將ab的類型也限制為整數:

instance (Integral a, Integral b) => Num (a -> b) where

創建Num的實例,我們需要提供(+)(-)(*)fromIntegral (實際上,我們也應該定義幾個其他函數,但是現在就不用擔心了)。

有一種相當自然的方法來定義加法,減法和乘法(此處的所有代碼構成Num實例的一部分,應相對於實例聲明縮進)

    f + g = \x -> f x + g x
    f - g = \x -> f x - g x
    f * g = \x -> f x * g x

即,當您添加兩個函數fg ,您將獲得一個將fg到其參數的新函數,然后將它們加在一起。 由於我們要求應用fg結果是整數類型,因此我們知道將它們的輸出相加是有意義的。

要將整數解釋為函數,我們可以編寫

    fromInteger n = \m -> fromIntegral m `mod` fromIntegral n

即,當我們有一個整數n ,我們返回一個參數m的函數,該函數在被調用時確保兩個參數的類型相同(通過對這兩個參數都調用fromIntegral ),然后將它們用作函數mod參數。

最后,還有更多樣板可以阻止Haskell抱怨:

    abs f = undefined
    signum f = undefined

我們可以測試一下。 我的代碼放在一個名為numfun.hs的文件中。 我啟動Haskell解釋器並加載我的文件:

Prelude> :l numfun.hs
[1 of 1] Compiling Main             ( numfun.hs, interpreted )
Ok, modules loaded: Main.

現在,我可以定義一些功能:

*Main> let f = (+ 1)
*Main> let g = (* 2)

我可以添加或減去它們:

*Main> (f + g) 3   -- (3+1) + (3*2)
10
*Main> (f - g) 3   -- (3+1) - (3*2)
-2

我可以將數字稱為函數:

*Main> 3 4         -- 4 `mod` 3
1

發生第一個錯誤是因為帶有4的整數文字可以是具有Num實例的任何類型。 也就是說, 4具有(Num a) => a的類型,因此它可以用作IntegerDoubleRational等。由於將3應用於自變量( 4 ),因此它在上下文中知道3必須為函數類型(例如,對於a0t0 a0 -> t0 t0 )。 但是沒有函數的Num實例,因此將3用作函數是無效的。 如果添加了instance Num (a -> b) ,它將可以工作,但您可能不想這樣做。

至於后者,這兩個錯誤消息是等效的。 GHC生成的名稱沒有特殊含義。 字母通常從您使用的函數類型中的類型變量派生,並附加數字以使內容明確。 在這種情況下,第二個表達式等效於(+) 2 (3 4) (因為函數應用程序比任何infix運算符都綁定得更緊密),它與您的第一段代碼並不完全相同。

暫無
暫無

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

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