映射newtype中的值

时间:2017-09-21 22:40:10

标签: haskell newtype

假设定义了以下newtype

newtype A a = A a

还有一个功能:

f :: A a -> A a

现在假设我定义了另一个newtype,其中包含A a

newtype B a = B (A a)

然后我想定义一个函数fb,该函数在B a上运行,但只使用f

fb :: B a -> B a
fb (B x) = B (f x)

现在这非常不方便,因为我必须将值解包并将值包装在B a类型的元素中。如果我只需要定义一个这样的fb,那就不会那么糟糕了,但是如果有很多这样的话会变得很乏味。

如果有一个带有函数的类型类,我会很高兴:

(<$$>) :: k a -> k b -> h (k a) -> h (k b)

这样fb可以改写为:

fb = (f <$$>)

也许这样的抽象已经存在,但我找不到它。

3 个答案:

答案 0 :(得分:4)

一种解决方案是使用newtype-generics包,尤其是over函数:

{-# LANGUAGE DeriveGeneric #-}
import Control.Newtype (Newtype, over)
import GHC.Generics

newtype A a = A a

newtype B a = B (A a) deriving (Generic)

instance Newtype (B a)

f :: A a -> A a
f = undefined

fb :: B a -> B a
fb = over B f

请注意,over需要外部B构造函数作为参数,而不仅仅是函数f

答案 1 :(得分:3)

如果你喜欢danidiaz的回答,你可能会更喜欢这个&#34;现代&#34;版本over

mover :: (Coercible o n, Coercible o' n')
      => (o -> n)
      -> (o' -> n')
      -> (o -> o') -> n -> n'
mover _pack _pack' = coerce

这会跳过所有实例,转而采用更明确的传递方式。

答案 2 :(得分:1)

实际上,使用newtype的一个主要原因是为包含在现有类型上的新类型派生新的类类实例。

所以很简单。您只需要为Functor类编写一个实例,就是这样。

newtype Team a = Team a deriving (Show , Eq)

instance Functor Team where
  fmap f (Team x) = Team (f x)

newtype League a = League (Team a) deriving (Show , Eq)

instance Functor League where
  fmap f (League x) = League (fmap f x)


upgradeTeam :: (Int -> Int) -> Team [Int] -> Team [Int]
upgradeTeam f = fmap (map f)

upgradeLeague :: (Int -> Int) -> League [Int] -> League [Int]
upgradeLeague f = fmap (map f) 

prependToLeague :: League [Int] -> Int -> League [Int]
prependToLeague x n = fmap (n:) x

*Main> upgradeTeam (+1) (Team [0,1,2,3])
Team [1,2,3,4]

*Main> upgradeLeague (*2) (League (Team [1,2,3,4]))
League (Team [2,4,6,8])

*Main> prependToLeague (League (Team [2,4,6,8])) 42
League (Team [42,2,4,6,8])