How to override Show instance of some basic types in Haskell?

后端 未结 4 1394
太阳男子
太阳男子 2021-01-18 05:19

I\'m writting some programs in Haskell, dealing with a lot of basic types like Word32/Word64 etc.. I use ghci to test the functions frequently, see the results in terminal.<

相关标签:
4条回答
  • 2021-01-18 05:52

    Agreeing with @ehird and @hammar that this could be abused. In the case of wanting some numbers to always show as hex, I think it's reasonable because "0xff" is a legitimate representation of a number. So this:

    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    
    module HexNumber where
    
    import Numeric
    import Text.Read
    import qualified Text.Read.Lex as L
    
    newtype HexInt a = HexInt { int :: a }
      deriving (Eq, Ord, Num, Enum)
    
    instance (Show a, Integral a) => Show (HexInt a) where 
      show hi = "0x" ++ showHex (int hi) ""
    
    instance (Num a) => Read (HexInt a) where
    -- Couldn't figure out how to write this instance so just copy/paste from Text.Read
      readPrec     = readNumber convertInt
      readListPrec = readListPrecDefault
      readList     = readListDefault
    
    readNumber :: Num a => (L.Lexeme -> ReadPrec a) -> ReadPrec a
    readNumber convert =
      parens
      ( do x <- lexP
          case x of
            L.Symbol "-" -> do y <- lexP
                                n <- convert y
                                return (negate n)
    
            _   -> convert x
      )
    
    convertInt :: Num a => L.Lexeme -> ReadPrec a
    convertInt (L.Number n)
    | Just i <- L.numberToInteger n = return (fromInteger i)
    convertInt _ = pfail
    

    Now I can:

    > let x = 10 :: HexInt Int
    > x
    0xa
    > x * 2
    0x14
    > let x = 10 :: HexInt Integer
    > x
    0xa
    > x * 2
    0x14
    > read "0xa" :: HexInt Int
    0xa
    > read "10" :: HexInt Int
    0xa
    

    This seems very useful to me working with low-level stuff a lot. Maybe I'll put it on Hackage.

    0 讨论(0)
  • 2021-01-18 06:02

    One extreme solution would be to use {-# LANGUAGE NoImplicitPrelude #-}, and import your own "Prelude" instead. That would probably be a lot more work than it's worth for your case, though.

    0 讨论(0)
  • 2021-01-18 06:14

    No, there is no way to achieve this without newtypes; instances cannot be overriden.

    If you really want this, I would suggest defining your own typeclass, ShowHex, like Show but with all the instances printing in hex. However, I would consider your Show instance incorrect; Show instances are designed for debugging and serialisation, and should output syntactically valid code.1 Yours doesn't, so I would suggest either defining your own typeclass for displaying these values, or simply using a function.

    Modifying the code to base for this is impractical; not only would this change in semantics for the instances break a lot of packages, but it'd be a huge pain to get GHC to actually use your modified version.

    1 Ideally, the code they produce should be semantically valid Haskell that produces a value comparing equal to show's input, but this is not strictly necessary.

    0 讨论(0)
  • 2021-01-18 06:15

    That would be abusing the Show instance. It's not really meant for formatting. If you want to show something in hexadecimal, just use a function to do the conversion. For example, you can use showHex from Numeric to make a small helper like this:

    > import Numeric
    Numeric> let hex x = showHex x ""
    Numeric> hex 123456
    "1e240"
    
    0 讨论(0)
提交回复
热议问题