繁体   English   中英

Haskell:newtype的Functor实例

[英]Haskell: Functor instance for newtype

要设置水平,这是一个简单的仿函数:

data Box a = Box a deriving (Show)

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

这允许我们“在盒子内”操作:

> fmap succ (Box 1)
Box 2

如何使用newtype实现同样的语法便利? 假设我有以下内容:

newtype Width  = Width  { unWidth  :: Int } deriving (Show)
newtype Height = Height { unHeight :: Int } deriving (Show)

这有点笨重:

> Width $ succ $ unWidth (Width 100)
Width {unWidth = 101}

这样会很好:

> fmap succ (Width 100)   -- impossible?
Width {unWidth = 101}

当然,我不能将Width或Height作为Functor的一个实例,因为它们都没有类型* -> * 虽然语法他们觉得没有比中的不同的,所以它看起来像它应该有可能没有所有的手工包装和展开对潜在价值进行操作。

另外,由于每个新newtype的重复,创建这样的n个函数并不令人满意:

fmapWidth  :: (Int -> Int) -> Width  -> Width
fmapHeight :: (Int -> Int) -> Height -> Height

如何将Ints上的函数提升为宽度函数?

首先请注意newtype在这里没有障碍 - 你可以像data一样参数化,然后你有一个普通的仿函数。 喜欢

{-# LANGUAGE DeriveFunctor #-}
newtype WidthF a = Width  { unWidth  :: a } deriving (Show, Functor)
type Width = WidthF Int

不过,我不认为这是一个好主意。 Width 不应该是算子; 将非数字类型存储在其中是没有意义的。

user2407038建议的一个选项是使其成为“单态仿函数”

import Data.MonoTraversable (MonoFunctor(..))

newtype Width = Width  { unWidth  :: Int } deriving (Show)

instance MonoFunctor Width where
  omap f (Width w) = Width $ f w

这对我来说似乎也不合理 - 如果您以一种通用方式映射数字操作,那么您也可以为Num等提供Width实例并直接使用它们。 但是你几乎没有比简单更好的类型系统保证

type Width = Int

这可以很容易地在没有任何帮助的情况下进行修改,另一方面是它很容易被错误处理而没有任何类型的系统保护措施。

相反,我认为你想要的可能是这样的:

import Control.Lens

data Box = Box {
   width, height :: Int }

widthInPx, heightInPx :: Lens' Box Int
widthInPx f (Box w h) = (`Box`h) <$> f w
heightInPx f (Box w h) = (Box w) <$> f h

那你可以做

> Box 3 4 & widthInPx %~ (*2)
Box 6 4
> Box 4 2 & heightInPx %~ succ
Box 4 3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM