首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在fmap上防止自定义类型中的错误值

在fmap上防止自定义类型中的错误值
EN

Stack Overflow用户
提问于 2016-09-05 19:20:03
回答 2查看 112关注 0票数 0

我想要一个12小时钟的单面图。

代码语言:javascript
运行
复制
data Clock12Hours = Clock12Hours Int
    deriving (Show)

instance Monoid Clock12Hours where
    mappend (Clock12Hours x) (Clock12Hours y) = Clock12Hours $ (x + y) `mod` 12
    mempty = Clock12Hours 12

当我mappend (Clock12Hours 4) (Clock12Hours 10)时,我得到了正确的值- Clock12Hours 2

我的问题是:

  1. 当我fmap (id) Clock12Hours 10的时候,我得到了Clock12Hours 10。但是,如果我没有提供函子定义(如注释中所阐明的,甚至无法完成),那么它如何知道如何实现fmap呢?
  2. 当我尝试fmap (+1) (Clock12Hours 10)时,我得到了一个错误No instance for (Num Clock12Hours) arising from a use of ‘+’ --为什么?
  3. 我在这里的目标是,如果我尝试fmap某些Int操作,它将给出一个类似于单样体的结果(例如,fmap (+4) (Clock12Hours 10)将返回Clock12Hours 2。多么?
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-05 21:45:43

我觉得你想做这样的事:

代码语言:javascript
运行
复制
{-# LANGUAGE Safe #-}
module Numeric.IntMod12 (
    IntMod12(), lift1Enum, lift2Enum
) where

newtype IntMod12 = IntMod12 Int deriving (Eq, Ord, Show)

instance Enum IntMod12 where
    {-# INLINABLE toEnum #-}
    toEnum i = IntMod12 (mod i 12)
    {-# INLINABLE fromEnum #-}
    fromEnum (IntMod12 i) = i

lift1Enum :: (Enum a, Enum b) => (Int -> Int) -> a -> b
{-# INLINABLE lift1Enum #-}
lift1Enum f = \ x -> toEnum (f (fromEnum x))

lift2Enum :: (Enum a, Enum b, Enum c) => (Int -> Int -> Int) -> a -> b -> c
{-# INLINABLE lift2Enum #-}
lift2Enum f = \ x y -> toEnum (f (fromEnum x) (fromEnum y))

instance Real IntMod12 where
    {-# INLINABLE toRational #-}
    toRational (IntMod12 i) = toRational i

instance Num IntMod12 where
    {-# INLINABLE fromInteger #-}
    fromInteger i = IntMod12 (fromInteger (mod i 12))
    (+) = lift2Enum (+)
    (-) = lift2Enum (-)
    (*) = lift2Enum (*)
    negate = lift1Enum negate
    abs = id
    signum 0 = 0
    signum _ = 1

instance Integral IntMod12 where
    {-# INLINABLE toInteger #-}
    toInteger (IntMod12 i) = toInteger i
    div = lift2Enum div
    mod = lift2Enum mod
    quot = lift2Enum quot
    rem = lift2Enum rem
    divMod x y = (toEnum d, toEnum m) where
        (d, m) = divMod (fromEnum x) (fromEnum y)
    quotRem x y = (toEnum q, toEnum r) where
        (q, r) = quotRem (fromEnum x) (fromEnum y)

instance Monoid IntMod12 where
    mempty = 0
    mappend = (+)

因为这不是Functor (并且不能转换为Functor),所以您必须使用类似lift1Enum的东西而不是fmap

票数 3
EN

Stack Overflow用户

发布于 2016-09-05 20:31:20

我没有1.的答案,因为我无法在GHC7.10.3上重现错误。

对于2.:只有使用一个类型参数来构造的类型才能是函子,因为fmap在其参数中是多态的,而Clock12Hours不是。

要回答3.:一个映射操作打开它的参数,应用一个函数并重新打包整个过程。使用这个定义,我们得出如下结论:

代码语言:javascript
运行
复制
myMap :: (Int -> Int) -> Clock12Hours -> Clock12Hours
myMap f (Clock12Hours time) = Clock12Hours (f time)

但是现在我们遇到的问题是,我们可能没有12小时的时间,所以我们有两个选项: add 0 (mappend mempty),或者编写normalize函数:

代码语言:javascript
运行
复制
normalize (Clock12Hours x) = Clock12Hours (x `mod` 12)
normalize clock = myMap (`mod` 12) clock
normalize = myMap (`mod` 12)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39336516

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档