2015-07-04 37 views
1

我正在嘗試編寫filterA :: (ArrowChoice arr) => arr a Bool -> arr [a] [a]函數,該函數從列表中刪除每個元素,其中f :: arr a Bool返回False。這是我迄今爲止Haskell Control.Arrow:試圖寫一個filterA函數

listcase [] = Left() 
listcase (x:xs) = Right (x, xs) 

filterA f = arr listcase >>> 
      arr (const []) ||| (first (f &&& arr id) >>> 
      arr (\((b,x),xs) -> if b then 
       x : (filterA f xs) 
       else filterA f xs 
      )) 

現在這一點也適用(->) a箭測試它的時候,像這樣:

λ> filterA (== 8) [8,9] 
[8] 

但是,它沒有工作,爲Kleisli箭似

λ> runKleisli (Kleisli $ filterA (== 8)) (return [8,9] :: [IO Int]) 

<interactive>:160:47: 
    Couldn't match expected type `IO Int' with actual type `[t0]' 
    In the first argument of `return', namely `[8, 9]' 
    In the second argument of `runKleisli', namely 
     `(return [8, 9] :: [IO Int])' 
    In the expression: 
     runKleisli (Kleisli $ filterA (== 8)) (return [8, 9] :: [IO Int]) 

當添加類型簽名filterA :: (Arrow arr) => arr a Bool -> arr [a] [a]filterA :: (ArrowChoice arr) => arr a Bool -> arr [a] [a]時,它會拋出此錯誤:

arrows.hs:11:22: 
    Could not deduce (arr ~ (->)) 
    from the context (Arrow arr) 
     bound by the type signature for 
       filterA :: Arrow arr => arr a Bool -> arr [a] [a] 
     at arrows.hs:7:12-51 
     `arr' is a rigid type variable bound by 
      the type signature for 
       filterA :: Arrow arr => arr a Bool -> arr [a] [a] 
      at arrows.hs:7:12 
    Expected type: [a] -> [a] 
     Actual type: arr [a] [a] 
    The function `filterA' is applied to two arguments, 
    but its type `arr a Bool -> arr [a] [a]' has only one 
    In the second argument of `(:)', namely `(filterA f xs)' 
    In the expression: x : (filterA f xs) 

我不明白爲什麼。我錯過了什麼?

編輯: @ jaket的評論工作(我猜這有點愚蠢)但類型簽名仍然不匹配。 我也更新了代碼更加緊湊(仍然得到同樣的錯誤雖然)

filterA f = arr listcase >>> 
      arr (const []) ||| (arr toEither >>> 
      (filterA f) ||| (second (filterA f) >>> arr uncurry (:))) 
    where toEither (x, xs) = if f x then Right (x, xs) else Left xs 

GHC推斷類型爲filterA :: (a -> Bool) -> [a] -> [a],順便說一句。

+0

'runKleisli(Kleisli $ filterA(== 8))[8,9]'? – jaket

+0

@jaket修復了我的猜測。然而,它並沒有修復類型簽名的東西 – tolUene

+0

在你的更新中,你使用'f'就好像它是'如果fx那麼...'中的函數那樣''使用'f'就是爲什麼GHC堅持這個函數只有當'arr'類型實際上是'( - >)'時,纔會檢測類型。請參閱下面的答案。 –

回答

1

你的問題是你'試圖在y的函數定義中進行遞歸OU與arr裹在裏面,你叫filterA f,就像它在這一行是一個函數:

   x : (filterA f xs) 

,只有當箭頭類型爲(->),這是什麼類型的錯誤之一是告訴你的作品。

相反,你需要做遞歸在箭頭的水平,如:

listcase :: [t] -> Either() (t, [t]) 
listcase [] = Left() 
listcase (x:xs) = Right (x, xs) 

filterA :: (ArrowChoice arr) => arr a Bool -> arr [a] [a] 
filterA f = listcase ^>> 
      arr (const []) ||| ((f &&& arr id) *** filterA f >>^ 
           (\((b, x), xs) -> if b then x:xs else xs)) 

(它編譯)

runKleisli例子是有點糊塗了,你的意思是說:

runKleisli (filterA $ Kleisli $ return . (== 8)) [8,9] 

runKleisli (filterA $ arr (== 8)) [8,9] :: IO [Int] 

這是直截了當地看待類型。

+0

謝謝你,這是有效的。我已經重寫了要使用的函數,或者像這樣:'filterA f = arr listcase >>> arr(const [])||| ((&&&& arr id)*** filterA f >>^ toEither >>>(arr id |||(arr uncurry(:))))and 'toEither((True,x),xs)= Right(x,xs)' 'toEither((False,_),xs)= Left xs',我觀察到一些我不完全明白的行爲。使用'>>^toEither'的版本起作用,而使用'>>> arr toEither'的版本不能編譯。但不是'>> ^'定義爲a >>^f = a >>> arr f'? – tolUene

+1

你的補償困境來自其他遠離你改變的東西,而不是你認爲他們做的類型。你想要'arr(uncurry(:))'或'(arr $ uncurry(:))' - 也就是說,不要直接向'uncurry'應用'arr'。如果你這樣做,那麼'>>> arr toEither'和'>>^toEither'變體將按預期進行編譯 –

+0

如果你正在嘗試使用通用箭頭,因爲'>> ^'和'>> >'右邊的相關(即,都是'infixr 1'),你要小心形式'a >>^b >>> c'的表達式,也就是說有'>>>'在'>> ^'的右邊,因爲這意味着和'a >> ^(b >>> c)'相同,所以''''箭頭將用在'( - >)'箭頭實例中,而不是整個功能所在的一般箭頭。 –

0

正如我在評論中提到:

runKleisli (Kleisli $ filterA (== 8)) [8, 9] 

接下來需要提起f :: a -> b成箭頭arr a b

(first (arr f &&& arr id) 
     ^^^ 
在功能

filterA :: ArrowChoice arr => (a -> Bool) -> arr [a] [a] 
filterA f = arr listcase >>> 
      arr (const []) ||| (first (f &&& arr id) >>> 
      arr (\((b,x),xs) -> if b then 
       x : (filterA f xs) 
       else filterA f xs 
      )) 
+0

但是通過我在帖子中提供的類型簽名,「f」已經是一個箭頭了。我會怎麼做呢? – tolUene

+0

f有'a - > Bool'類型。看我的編輯。 – jaket

+0

哦,等一下。我明白你在說什麼。稍等片刻。 – jaket

1

只是爲了補充其他答案:使用Arrow syntax(也見GHC手冊,第Arrow notation)你可以寫函數稍微更可讀:

{-# LANGUAGE Arrows #-} 

import Control.Arrow 

filterA :: (ArrowChoice arr) => arr a Bool -> arr [a] [a] 
filterA f = farr 
    where 
    farr = proc xs -> 
      case xs of 
       []  -> returnA -< [] 
       (x:xs') -> do 
        b <- f -< x 
        ys' <- farr -< xs' 
        returnA -< if b then x : ys' else ys' 

內部轉換箭頭符號的結果很可能是稍微簡潔一些,但希望編譯器能夠爲您優化。