Haskell 在newtype中映射值
假设定义了以下Haskell 在newtype中映射值,haskell,newtype,Haskell,Newtype,假设定义了以下newtype: newtype A a = A a 还有一个功能: f :: A a -> A a (<$$>) :: k a -> k b -> h (k a) -> h (k b) {-# LANGUAGE DeriveGeneric #-} import Control.Newtype (Newtype, over) import GHC.Generics newtype A a = A a newtype B a = B (A
newtype
:
newtype A a = A a
还有一个功能:
f :: A a -> A a
(<$$>) :: k a -> k b -> h (k a) -> h (k b)
{-# 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
现在假设我定义了另一个newtype
,它包含A
:
newtype B a = B (A a)
然后我想定义一个函数fb
,它在ba
上运行,但只使用f
:
fb :: B a -> B a
fb (B x) = B (f x)
现在这很不方便,因为我必须在类型为ba
的元素中展开并包装值。如果我只需要定义一个这样的fb
,这就不会那么糟糕了,但是如果有很多这样的函数,这就变得相当乏味了
如果有一个带有函数的类型类,我会很高兴:
f :: A a -> A a
(<$$>) :: k a -> k b -> h (k a) -> h (k b)
{-# 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
可能这样的抽象已经存在,但我找不到它。实际上使用
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])
一种解决方案是使用软件包,尤其是功能:
f :: A a -> A a
(<$$>) :: k a -> k b -> h (k a) -> h (k b)
{-# 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
如果您喜欢danidiaz的答案,您可能会喜欢这个更“现代”的over
:
mover :: (Coercible o n, Coercible o' n')
=> (o -> n)
-> (o' -> n')
-> (o -> o') -> n -> n'
mover _pack _pack' = coerce
这跳过了所有的实例,而支持更显式的传递。对于新类型的特殊情况,您应该只使用
Data.concure
,它提供了concure::(a->a)->a->a和concure:(a->a)->ba->ba和concure(a->a)->B a->B a
。我认为这是自动包装/展开的方法。@user2407038,确实如此,但这不一定要在使用站点上。对于一个新类型,实例函子N,其中fmap=concure
非常合理。我看不出你建议的类型类会有什么帮助。类型ba->ba
与h(ka)->h(kb)
不统一,这让人想起newtype/newtype泛型包中的over
。但是,over
需要外部newtype构造函数作为附加参数。我更可能接触到Control.Lens.Iso.compressed
以及Control.Lens.Setter.over
。它们不需要Newtype
或Generic
实例。实际上,您可以更简单地完成这项工作。请看我将要写的答案。这在我的情况下不起作用,因为fmap::(a->b)->fa->fb
。我想要重用的函数的类型是aaa->ab
,因此我需要类似?::(A->A b)->b A->b A
。请注意,我不是在a
类型的元素上操作(在您的示例中),而是在Team a
类型的元素上操作。对于我正在处理的问题A A=Map A Integer
,所以这个例子不起作用。另一个答案中的函数over
。@Damian NadalesA->AB
实际上是fmap g
在A
类型上的部分应用,其中g
是类型(A->b)
所以:t fmap g
会给你(f A->f b)
其中f
表示函子A
。然后,您只需应用另一个fmap
,但这次是在类型B
上,如fmap(fmap g)
或“fmap”。fmap$g`这实际上是在上述代码中键入的联盟的函子实例中发生的情况。如果你喜欢联盟的函子实例(你的B类型)直接在(A A->A B)
上操作,而不是(A->B)
,那么让联盟的函子实例像fmap f(League x)=League(f x)
一样。在哪里定义了\u pack
?此外,我还希望在某些库中已经定义了mover
,这些只是通配符。您可以编写mover\uuu=强制
。