2014-09-10 47 views
12

我想通過在Haskell中實現它來了解鏡頭。我已經實現了view組合子如下:哈斯克爾鏡頭:如何使視圖與遍歷很好地發揮作用?

{-# LANGUAGE RankNTypes #-} 

import Control.Applicative 
import Data.Traversable 

type Lens s a = Functor f => (a -> f a) -> s -> f s 

view :: Lens s a -> s -> a 
view lens = getConst . lens Const 

然而,當我嘗試結合使用它與traverse我收到以下錯誤信息:

Prelude> :load Lens.hs 
[1 of 1] Compiling Main    (Lens.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> :t view traverse 

<interactive>:1:6: 
    Could not deduce (Applicative f) arising from a use of ‘traverse’ 
    from the context (Traversable t) 
     bound by the inferred type of it :: Traversable t => t a -> a 
     at Top level 
    or from (Functor f) 
     bound by a type expected by the context: 
       Functor f => (a -> f a) -> t a -> f (t a) 
     at <interactive>:1:1-13 
    Possible fix: 
     add (Applicative f) to the context of 
     a type expected by the context: 
      Functor f => (a -> f a) -> t a -> f (t a) 
     or the inferred type of it :: Traversable t => t a -> a 
    In the first argument of ‘view’, namely ‘traverse’ 
    In the expression: view traverse 

不幸的是,我不明白這個錯誤信息。請解釋它的含義以及我如何解決這個問題。

回答

10

至於其他的答案已經解釋的問題是,view期待的東西,對於任何Functor f工作,但traverse只有fApplicative工作(也有仿函數不屬於應用性)。

lens,問題,是通過view類型不拿Rank2爭論解決(事實上,在鏡頭的大部分功能不使用鏡頭類型代名詞,他們總是用一些弱)。爲了您的功能,請注意view只使用f ~ Const。這就是爲什麼你可以改變類型簽名:

view :: ((a -> Const a a) -> s -> Const a s) -> s -> a 

實施,可以保持不變,但現在view也適用於traverse

view traverse :: (Traversable t, Monoid a) => t a -> a 

注意額外Monoid約束。出現此限制是因爲如果在traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a)中設置f ~ Const a,則需要實例Applicative (Const a)。不過,該實例在a上有一個Monoid約束。這也是有道理的,因爲遍歷可能是空的或包含多個元素,所以需要memptymappend

+1

很好的回答。非常感謝你爲這個問題提供了一個明智的解決方案。我確實有一個問題:'view'的類型簽名不會變成'((a - > Const a a) - > s - > Const a s) - > s - > a'? – 2014-09-11 02:29:54

+0

@AaditMShah哦,你說得對。我會糾正這一點 – bennofs 2014-09-11 14:39:04

3

traverse有此類型:

traverse :: (Applicative f, Traversable t) => (x -> f y) -> t x -> f (t y) 

如果我們的Lens明確的類型定義使自由變量f,它的定義實際上是

type Lens s a = forall f . Functor f => (a -> f a) -> s -> f s 

所以view希望可以操作的功能任何Functor,而traverse只能運行在Applicative

只需將Functor更改爲Applicative中的Lens的定義即可修復該錯誤,但我不確定這是否正是您希望在此處實現的。

+2

在此處將'Functor'改爲'Applicative'會給你一個'遍歷',不是嗎?我想這給出了「Traversal」這個名字來自哪裏的一些直覺。 – 2014-09-11 01:05:51

3

TL;博士 - 根據您的Lens定義,traverse不能是Lens因爲traverse並不適用於所有Functor的工作。


讓我們來看看你的類型:

λ :set -XRankNTypes 
λ :m +Control.Applicative Data.Traversable 
λ type Lens s a = Functor f => (a -> f a) -> s -> f s 
λ :t traverse 
traverse 
    :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) 

現在,在這一點上,我們可以看到,traverse,在一個方式,略高於我們的Lens型更普遍的 - 它可以採取功能 從a -> f b我們的鏡頭只能使用a -> f a的功能。

它限制對這種情況下是沒有問題的,所以我們可以說

λ :t traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a) 
traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a) 
    :: (Applicative f, Traversable t) => (a -> f a) -> t a -> f (t a) 

所以,現在很明顯,如果traverse是成爲Lens,它必須是一個Lens (t a) a,因爲這是做的唯一途徑類型變量排隊。

所以我們來試試看。

λ :t traverse :: Lens (t a) a 

<interactive>:1:1: 
    Could not deduce (Traversable t1) arising from a use of `traverse' 
    from the context (Functor f) 
     bound by the inferred type of 
       it :: Functor f => (a -> f a) -> t a -> f (t a) 
     at Top level 
    or from (Functor f1) 
     bound by an expression type signature: 
       Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     at <interactive>:1:1-24 
    Possible fix: 
     add (Traversable t1) to the context of 
     an expression type signature: 
      Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     or the inferred type of 
      it :: Functor f => (a -> f a) -> t a -> f (t a) 
    In the expression: traverse :: Lens (t a) a 

自己,它不喜歡那樣。哦,等等,爲了使用traverse我們的t型號必須是Traversable,所以讓我們添加這個限制。(就像「可能修復」)提出:

λ :t traverse :: Traversable t => Lens (t a) a 

<interactive>:1:1: 
    Could not deduce (Applicative f1) arising from a use of `traverse' 
    from the context (Functor f, Traversable t) 
     bound by the inferred type of 
       it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a) 
     at Top level 
    or from (Traversable t1, Functor f1) 
     bound by an expression type signature: 
       (Traversable t1, Functor f1) => 
       (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     at <interactive>:1:1-41 
    Possible fix: 
     add (Applicative f1) to the context of 
     an expression type signature: 
      (Traversable t1, Functor f1) => 
      (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1) 
     or the inferred type of 
      it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a) 
    In the expression: traverse :: Traversable t => Lens (t a) a 

好了,所以現在的問題是,它不能推斷fApplicative(也需要使用traverse),只是它是一個Functor (它從Lens的定義中得到)。

雖然我們無法將Applicative f添加到上下文中,但隱藏了f。當我們說type Lens s a = Functor f => (a -> f a) -> s -> f s,我們說Lens必須工作所有Functor s。

但是traverse只適用於Functor的子集也是Applicative。因此,traverse的類型是更具體的Lens es允許的類型。