簡體   English   中英

Haskell:使用兩個浮動參數組合函數失敗

[英]Haskell: composing function with two floating arguments fails

我正在嘗試組合類型為(Floating a) => a -> a -> a的函數與類型為(Floating a) => a -> a的函數以獲得類型為(Floating a) => a -> a -> a的函數(Floating a) => a -> a -> a 我有以下代碼:

test1 :: (Floating a) => a -> a -> a
test1 x y = x

test2 :: (Floating a) => a -> a
test2 x = x

testBoth :: (Floating a) => a -> a -> a
testBoth = test2 . test1
--testBoth x y = test2 (test1 x y)

但是,當我在 GHCI 中編譯它時,出現以下錯誤:

/path/test.hs:8:11:
    Could not deduce (Floating (a -> a)) from the context (Floating a)
      arising from a use of `test2'
                   at /path/test.hs:8:11-15
    Possible fix:
      add (Floating (a -> a)) to the context of
        the type signature for `testBoth'
      or add an instance declaration for (Floating (a -> a))
    In the first argument of `(.)', namely `test2'
    In the expression: test2 . test1
    In the definition of `testBoth': testBoth = test2 . test1
Failed, modules loaded: none.

請注意, testBoth的注釋版本testBoth編譯。 奇怪的是,如果我從所有類型簽名中刪除(Floating a)約束,或者如果我將test1更改為只使用x而不是xytestBoth編譯。

我搜索了 StackOverflow、Haskell wiki、Google 等,但沒有發現任何關於與此特定情況相關的函數組合限制的信息。 有誰知道為什么會這樣?

   \x y -> test2 (test1 x y)
== \x y -> test2 ((test1 x) y)
== \x y -> (test2 . (test1 x)) y
== \x -> test2 . (test1 x)
== \x -> (test2 .) (test1 x)
== \x -> ((test2 .) . test1) x
== (test2 .) . test1

這兩件事彼此不一樣。

   test2 . test1
== \x -> (test2 . test1) x
== \x -> test2 (test1 x)
== \x y -> (test2 (test1 x)) y
== \x y -> test2 (test1 x) y

您的問題與Floating無關,但事實上您想以不進行類型檢查的方式組合一個帶有兩個參數的函數和一個帶有一個參數的函數。 我會給你一個組合函數reverse . foldr (:) []的例子reverse . foldr (:) [] reverse . foldr (:) []

reverse . foldr (:) [] reverse . foldr (:) []具有[a] -> [a]類型並按預期工作:它返回一個反向列表( foldr (:) []本質上是列表的id )。

然而reverse . foldr (:) reverse . foldr (:)不進行類型檢查。 為什么?

當類型匹配函數組合時

讓我們回顧一些類型:

reverse      :: [a] -> [a]
foldr (:)    :: [a] -> [a] -> [a]
foldr (:) [] :: [a] -> [a]
(.)          :: (b -> c) -> (a -> b) -> a -> c

reverse . foldr (:) [] reverse . foldr (:) [] ,因為(.)實例化為:

(.) :: ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]

換句話說,在(.)類型注釋中:

  • a變成[a]
  • b變成[a]
  • c變成[a]

所以reverse . foldr (:) [] reverse . foldr (:) []的類型為[a] -> [a]

當類型與函數組合不匹配時

reverse . foldr (:) reverse . foldr (:)不進行類型檢查,因為:

foldr (:) :: [a] -> [a] -> [a]

作為(.)的正確操作符,它將實例化其類型從a -> b[a] -> ([a] -> [a]) 也就是說,在:

(b -> c) -> (a -> b) -> a -> c
  • 類型變量a將替換為[a]
  • 類型變量b將替換為[a] -> [a]

如果foldr (:)類型是a -> b(. foldr (:))的類型將是:

(b -> c) -> a -> c`

foldr (:)用作(.)的右操作數)。

但是因為foldr (:)類型是[a] -> ([a] -> [a])(. foldr (:))的類型是:

(([a] -> [a]) -> c) -> [a] -> c

reverse . foldr (:) reverse . foldr (:)不進行類型檢查,因為reverse的類型為[a] -> [a] ,而不是([a] -> [a]) -> c

貓頭鷹運營商

當人們第一次在 Haskell 中學習函數組合時,他們了解到當函數體的最右邊有函數的最后一個參數時,你可以從參數和函數體中刪除它,替換或括號(或美元符號) ) 帶點。 換句話說,以下 4 個函數定義是等效的

f a x xs = g ( h a ( i x   xs))
f a x xs = g $ h a $ i x   xs
f a x xs = g . h a . i x $ xs
f a x    = g . h a . i x

所以人們有一種直覺說“我只是從主體和參數中刪除最右邊的局部變量”,但這種直覺是錯誤的,因為一旦你刪除了xs

f a x = g . h a . i x
f a   = g . h a . i

不等價 您應該了解函數組合何時進行類型檢查,何時不進行類型檢查。 如果上面的2個是等價的,那么就意味着下面的2個也是等價的:

f a x xs = g . h a . i x $ xs
f a x xs = g . h a . i $ x xs

這是沒有意義的,因為x不是一個以xs作為參數的函數。 x是函數i的參數, xs是函數(ix)的參數。

有一個技巧可以使具有2 個參數的函數無點。 那就是使用“owl”運算符:

f a x xs = g . h a .  i x xs
f a      = g . h a .: i
  where (.:) = (.).(.)

以上兩個函數定義是等價的。 閱讀有關“owl”運算符的更多信息

參考

一旦你理解了函數、類型、部分應用和柯里化、函數組合和美元運算符,Haskell 編程就會變得更加簡單和直接。 要確定這些概念,請閱讀以下 StackOverflow 答案:

另請閱讀:

您的問題與Floating沒有任何關系,盡管 typeclass 確實使您的錯誤更難以理解。 以下面的代碼為例:

test1 :: Int -> Char -> Int
test1 = undefined

test2 :: Int -> Int
test2 x = undefined

testBoth = test2 . test1

什么是testBoth的類型? 好吧,我們取(.) :: (b -> c) -> (a -> b) -> a -> c並轉動曲柄得到:

  1. b ~ Inttest2的參數與(.)的第一個參數統一)
  2. c ~ Inttest2的結果與(.)的第一個參數的結果統一)
  3. a ~ Inttest1參數 1 與(.)參數 2 統一)
  4. b ~ Char -> Inttest1結果與(.)參數 2 統一)

可是等等! 該類型變量 'b' (#4, Char -> Int ) 必須與test2 (#1, Int ) 的參數類型統一。 不好了!

你應該怎么做? 正確的解決方法是:

testBoth x = test2 . test1 x

還有其他方法,但我認為這是最易讀的。

編輯:那么試圖告訴你的錯誤是什么? 據說統一Floating a => a -> aFloating b => b需要一個instance Floating (a -> a) ...雖然這是真的,但你真的不希望 GHC 嘗試將函數視為一個浮點數。

暫無
暫無

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

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