2017-01-02 77 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),供参考 –

相关问题