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

时间:2016-09-05 19:20:03

标签: haskell

我想要一个12小时的幺半。

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操作,它会给出类似于monoid的结果(例如fmap (+4) (Clock12Hours 10)将返回Clock12Hours 2。如何?

2 个答案:

答案 0 :(得分:3)

我认为你打算做这样的事情:

{-# 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之类的内容。

答案 1 :(得分:0)

我对1.没有答案,因为我无法重现GHC 7.10.3上的错误。

对于2.:只有采用1个类型参数构建的类型才能成为仿函数,因为fmap在其参数中是多态,而Clock12Hours则不是。3.

回答myMap :: (Int -> Int) -> Clock12Hours -> Clock12Hours myMap f (Clock12Hours time) = Clock12Hours (f time) :地图操作解压缩它的参数,应用函数并重新打包整个事物。使用这个定义,我们提出:

mappend mempty

但现在我们遇到的问题是我们可能无法获得12小时的时间,因此我们有两个选择:添加0(normalize)或编写normalize (Clock12Hours x) = Clock12Hours (x `mod` 12) normalize clock = myMap (`mod` 12) clock normalize = myMap (`mod` 12) 函数:

private void button1_Click(object sender, EventArgs e)
{
    Form2 f2= new Form2();
    if(condition==true)
    {
        this.Hide();
        f2.ShowDialog();
        this.Close();
    }
}