How to create many bounded types without duplicating code in Haskell?

孤街浪徒 提交于 2019-12-08 04:05:49

问题


For a project, I’ve created a type based on Int which throws an error whenever the program tries to use a value beyond limits ([0..127] in my case). The code below does this and it works for me.

Is it possible in Haskell to create a second bounded type (say [0..255] for example) without duplicating this code ?

Thanks for your answers

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Minitel.Type.MNatural (MNat, mnat, fromMNat) where

-- | The MNat type. The constructor is hidden.
newtype MNat = MakeMNat Int deriving (Real, Eq, Ord, Show)

-- | MNat is a bounded type
instance Bounded MNat where
    minBound = MakeMNat 0
    maxBound = MakeMNat 127

-- | Converts an Int into an MNat
mnat :: Int -> MNat
mnat x | loLimit <= x && x <= hiLimit = MakeMNat x
       | otherwise = error "Number out of bounds"
       where loLimit = fromIntegral (minBound :: MNat)
             hiLimit = fromIntegral (maxBound :: MNat)

-- | Converts an MNat into an Int
fromMNat :: MNat -> Int
fromMNat (MakeMNat i) = i

-- | Converts an Int binary function returning Int to a MNat binary function
--   returning an MNat
mfnat :: (Int -> Int) -> (MNat -> MNat)
mfnat f = mnat . f . fromMNat

mfnat2 :: (Int -> Int -> Int) -> (MNat -> MNat -> MNat)
mfnat2 f x y = mnat $ f (fromMNat x) (fromMNat y)

-- | You can do additions, substractions and multiplication with MNat
instance Num MNat where
    fromInteger = mnat . fromIntegral
    (+)         = mfnat2 (+)
    (-)         = mfnat2 (-)
    (*)         = mfnat2 (*)
    abs         = mfnat abs
    signum      = mfnat signum

-- | Allows to use toInteger with MNat
instance Integral MNat where
    quotRem x y = (fromInteger $ quot x' y', fromInteger $ rem x' y')
                  where (x', y') = (toInteger x, toInteger y)
    toInteger   = toInteger . fromMNat

-- | Allows to generate lists
instance Enum MNat where
    toEnum      = mnat
    fromEnum    = fromMNat

Note:

  • performance is not a matter here

回答1:


You can do it in GHC 7.8 using type-level literals:

{-# LANGUAGE DataKinds, PolyKinds, ScopedTypeVariables #-}
module SO26723035 where

import GHC.TypeLits
import Data.Proxy

newtype MNat (n :: Nat) = MakeMNat Int deriving (Eq, Ord, Show)

instance KnownNat n => Bounded (MNat n) where
  minBound = MakeMNat 0
  maxBound = MakeMNat . fromInteger $ natVal (Proxy :: Proxy n)

 

ghci> maxBound :: MNat 5
MakeMNat 5

You can use type synonyms to fix individual types. The rest of your code compiles fine with this polymorphic MNat with mechanical changes. You have to add the KnownNat context everywhere and use ScopedTypeVariables in mnat.



来源:https://stackoverflow.com/questions/26723035/how-to-create-many-bounded-types-without-duplicating-code-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!