我们说我有以下newtype
:
newtype Foo = Foo Integer deriving (Eq, Show)
是否有简洁的方法可以添加两个Foo
&#39>:
(Foo 10) + (Foo 5) == Foo 15
或获取最大值:
max (Foo 10) (Foo 5) == Foo 5
?
我很好奇是否可以轻松地将a
的功能用于newtype a
而不是:
addFoo :: Foo -> Foo -> Foo
addFoo (Foo x) (Foo y) = Foo $ x + y
答案 0 :(得分:12)
正如haskell98知道如何为您推导Eq
和Show
个实例一样,您可以启用GeneralizedNewtypeDeriving
扩展名为ghc来获取Num
和{{ 1}}你需要的实例:
Ord
答案 1 :(得分:10)
您想要将Integer -> Integer -> Integer
类型的功能提升为Foo -> Foo -> Foo
。为此,您可以定义实用程序功能:
liftFoo :: (Integer -> Integer) -> Foo -> Foo
liftFoo f (Foo a) = Foo $ f a
liftFoo2 :: (Integer -> Integer -> Integer) -> Foo -> Foo -> Foo
liftFoo2 f (Foo a) (Foo b) = Foo $ f a b
-- and so on
然后您可以按如下方式使用它:
liftFoo2 (+) (Foo 10) (Foo 5)
liftFoo2 max (Foo 10) (Foo 5)
这样做的好处是不需要扩展。
另一种选择是让Foo
newtype的定义更加允许,以便您可以将其设为Functor
和Applicative
的实例:
import Control.Applicative
newtype Foo a = Foo a deriving (Eq, Show)
foo :: Integer -> Foo Integer
foo = Foo
instance Functor Foo where
fmap f (Foo a) = Foo $ f a
instance Applicative Foo where
pure = Foo
(Foo f) <*> (Foo a) = Foo $ f a
现在您可以执行以下操作:
(+) <$> foo 10 <*> foo 5
max <$> foo 10 <*> foo 5
由于foo
专门针对Integer
类型,因此您不会失去任何类型检查的好处。
答案 2 :(得分:3)
您也可以使用安全强制。大致您使用Data.Coerce.coerce
自动换行/解包新类型。
> import Data.Coerce
> newtype Foo = Foo Integer deriving (Eq, Show, Ord)
> coerce (Foo 1) :: Integer
1
> let f :: Integer -> Integer ; f x = x + 1
> coerce f (Foo 10)
Foo 11
> coerce (succ :: Integer -> Integer) (Foo 10) :: Foo
Foo 11
> coerce (max :: Integer -> Integer -> Integer) (Foo 10) (Foo 5) :: Foo
Foo 10
请注意,它对于f
的单态函数非常有用,但对于succ
这样的多态函数则不太适用,因为在后一种情况下需要使用类型注释。
答案 3 :(得分:2)
要获得数学运算,您需要使Foo
成为Num
类型类的实例。这意味着您必须定义(+)
,(*)
,abs
,signum
,fromInteger
以及negate
或{{1} } (-)
。一旦定义了这些函数,您将获得免费在Foo
上运行的其他函数。
要让Num
和类似功能发挥作用,您需要将max
设为Foo
的实例。这需要定义Ord
或compare
。
通常,您可以在ghci中使用(<=)
来查找函数的类型,其中包括与其一起使用的类型类。然后,您只需确定必须为该类型类定义的最小函数集。
答案 4 :(得分:0)
有几个库提供组合器,用于将基础类型上的功能提升为新类型。
对于您给出的示例,可以使用newtype-generics
中类型为{p>的over2
组合器
over2 :: (Newtype n, Newtype n', o' ~ O n', o ~ O n) => (o -> n) -> (o -> o -> o') -> n -> n -> n'
或来自coercible-utils
类型的
over2 :: (Coercible a b, Coercible a' b') => (a -> b) -> (a -> a -> a') -> b -> b -> b'
用法(对于coercible-utils
中的版本)是这样的:
λ over2 Foo (+) (Foo 10) (Foo 5) :: Foo
Foo 15