2017-01-02 76 views
2

現在我有這個小幫手:對元組的所有值的任何內置fmap-sort?

both f (one,two) = (f one , f two) 

然後我在無聊地想知道是否fmap 「迭代」 的元組,所以我問GHCI:

fmap reverse ("aA","bB") 

結果是:

("aA","Bb") 

奇怪!因此,如果你願意,元組上的fmap的語義似乎是「將func應用於snd」。

任何在basePrelude我應該使用而不是我自己的both? Hoogle給了no promising results,或者我錯誤地解析了他們。

+4

「奇怪!」 - 它實際上是唯一可能的實現,但是在數據構造函數上使用的元組類型的語法隱藏了它。更直接的將是:'數據元組a b =元組a b'; '實例Functor(Tuple a)'...所以元組上'fmap'的具體類型簽名是(爲了清晰起見,有冗餘的元素):'fmap ::(b - > c) - >(Tuple a)b - > (元組a)c'其中'元組a'是你的'f'。 – jberryman

+1

當我第一次遇到它時,我將驗證這個「最後的類型參數」業務確實很奇怪。 Haskell對參數的順序有些敏感,特別是類型參數,這與基本上所有其他語言並不重要的語言完全不同。 – luqui

回答

9

在元組的兩個元素上「映射」方面,您所擁有的最好是Data.Bifunctor(現在爲base!)中定義的bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d。就這樣,你可以寫

ghci> bimap reverse reverse ("aA","bB") 
("Aa","Bb") 

或者,你可以(使用Arrow (->)實例特別是)使用Control.Arrow定義(***) :: Arrow a => a b c -> a b' c' -> a (b, b') (c, c')。就這樣,你可以寫

ghci> (reverse *** reverse) ("aA","bB") 
("Aa","Bb") 

但是,如果你想一個元組的兩個元素的同時映射了,也許你正在尋找一個抽象a類型的兩個值的組在一起。如果是這種情況,我建議您在(a,a)左右使用newtype。然後,您可以打開-XDeriveFunctor擴展爲得到以下

ghci> :set -XDeriveFunctor 
ghci> newtype Pair a = Pair (a,a) deriving (Show,Functor) 
ghci> fmap reverse (Pair ("aA","bB")) 
Pair ("Aa","Bb") 
4

你需要考慮完整的類型。在類型(a,b)(與s (,) a b相同)中,仿函數爲(,) a。因此,我們得到

fmap :: (b -> c) -> (,) a b -> (,) a c 

這清楚地表明,第一部分是由fmap不受影響。

> fmap reverse (True,"bB") 
(True,"Bb") 

你可能在想另一個函子

-- user-defined 
data Pair a = Pair a a deriving Functor 

這裏我們確實有

fmap :: (b -> c) -> Pair b -> Pair c 

既影響的組件,但這種類型的不是正規的元組的類型。

獎金之謎:出於同樣的原因

> length (1,2) 
1 
4

這肯定是這個特殊的例子矯枉過正,這是不是在基地,但最通用的編程庫可以讓你做到這一點。例如。與traverse-with-class

{-# LANGUAGE ImplicitParams #-} 
import Data.Generics.Traversable 
import Data.Proxy 

main = do 
    -- specify the constraint to make 'reverse' type-check. 
    -- (~) String means "equal to String" 
    let ?c = Proxy :: Proxy ((~) String) 
    print $ gmap reverse ("aA","bB") 
3

亞歷克表示,bimap有利於對。如果你想更一般地處理元組,一種選擇是使用一個類。下面的靈感來自於lens(特別是Control.Lens.Tuple)的一些想法,但我不認爲它與其中的任何想法完全相同。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE TypeFamilies #-} 

module Tuplish where 
import Data.Functor.Identity (Identity (..)) 
import Data.Profunctor.Unsafe ((#.), (.#)) 

class Tuply s t a b | s -> a, t -> b, s b -> t, t a -> s where 
    ttraverse :: Applicative f => (a -> f b) -> s -> f t 

mapTuple :: Tuply s t a b => (a -> b) -> (s -> t) 
mapTuple = (runIdentity .) #. ttraverse .# (Identity .) 


instance (a1 ~ a, a2 ~ a, b1 ~ b, b2 ~ b) 
    => Tuply (a1, a2) (b1, b2) a b where 
    ttraverse f (x,y) = (,) <$> f x <*> f y 

instance (a1 ~ a, a2 ~ a, a3 ~ a, b1 ~ b, b2 ~ b, b3 ~ b) 
    => Tuply (a1,a2,a3) (b1,b2,b3) a b where 
    ttraverse f (x,y,z) = (,,) <$> f x <*> f y <*> f z 

instance (a1 ~ a, a2 ~ a, a3 ~ a, a4 ~ a, b1 ~ b, b2 ~ b, b3 ~ b, b4 ~ b) 
    => Tuply (a1,a2,a3,a4) (b1,b2,b3,b4) a b where 
    ttraverse f (x,y,z,w) = (,,,) <$> f x <*> f y <*> f z <*> f w 

instance (a1 ~ a, a2 ~ a, a3 ~ a, a4 ~ a, a5 ~ a, 
      b1 ~ b, b2 ~ b, b3 ~ b, b4 ~ b, b5 ~ b) 
    => Tuply (a1,a2,a3,a4,a5) (b1,b2,b3,b4,b5) a b where 
    ttraverse f (x,y,z,w,u) = (,,,,) <$> f x <*> f y <*> f z <*> f w <*> f u 

instance (a1 ~ a, a2 ~ a, a3 ~ a, a4 ~ a, a5 ~ a, a6 ~ a, 
      b1 ~ b, b2 ~ b, b3 ~ b, b4 ~ b, b5 ~ b, b6 ~ b) 
    => Tuply (a1,a2,a3,a4,a5,a6) (b1,b2,b3,b4,b5,b6) a b where 

    ttraverse f (x,y,z,w,u,v) = (,,,,,) <$> f x <*> f y <*> f z <*> f w <*> f u <*> f v 
+1

[該鏡頭模塊](https://hackage.haskell.org/package/lens-4.15.1/docs/Control-Lens-Tuple.html),供參考 –

相關問題