简体   繁体   English

Haskell:newtype的Functor实例

[英]Haskell: Functor instance for newtype

To level set, here's a simple functor: 要设置水平,这是一个简单的仿函数:

data Box a = Box a deriving (Show)

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

This allows us to operate "inside the box": 这允许我们“在盒子内”操作:

> fmap succ (Box 1)
Box 2

How do I achieve this same syntactic convenience with a newtype? 如何使用newtype实现同样的语法便利? Let's say I have the following: 假设我有以下内容:

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

This is a bit clunky: 这有点笨重:

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

This would be nice: 这样会很好:

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

Of course, I can't make Width or Height an instance of a Functor since neither has kind * -> * . 当然,我不能将Width或Height作为Functor的一个实例,因为它们都没有类型* -> * Although, syntactically they feel no different than Box, and so it seems like it should be possible to operate on the underlying value without all of the manual wrapping and unwrapping. 虽然语法他们觉得没有比中的不同的,所以它看起来像它应该有可能没有所有的手工包装和展开对潜在价值进行操作。

Also, it isn't satisfying to create n functions like this because of the repetition with every new newtype: 另外,由于每个新newtype的重复,创建这样的n个函数并不令人满意:

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

How do I lift a function on Ints to be a function on Widths? 如何将Ints上的函数提升为宽度函数?

First note that newtype is no hurdle here – you can parameterise these just as well as data , and then you have an ordinary functor. 首先请注意newtype在这里没有障碍 - 你可以像data一样参数化,然后你有一个普通的仿函数。 Like 喜欢

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

I wouldn't consider that a good idea, though. 不过,我不认为这是一个好主意。 Width shouldn't be a functor; Width 不应该是算子; it doesn't make sense to store non-number types in it. 将非数字类型存储在其中是没有意义的。

One option as user2407038 suggests is to make it a “monomorphic functor” user2407038建议的一个选项是使其成为“单态仿函数”

import Data.MonoTraversable (MonoFunctor(..))

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

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

That too doesn't seem sensible to me – if you map number operations thus in one generic way , then you might just as well give Width instances for Num etc. and use these directly. 这对我来说似乎也不合理 - 如果您以一种通用方式映射数字操作,那么您也可以为Num等提供Width实例并直接使用它们。 But then you hardly have better type-system guarantees than a simple 但是你几乎没有比简单更好的类型系统保证

type Width = Int

which can easily be modified without any help , the flip side being that it can easily be mishandled without any type system safeguards. 这可以很容易地在没有任何帮助的情况下进行修改,另一方面是它很容易被错误处理而没有任何类型的系统保护措施。

Instead, I think what you want is probably this: 相反,我认为你想要的可能是这样的:

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

Then you can do 那你可以做

> 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