如何为新类型重写 show 函数?

9

我想重写 Haskell 中的默认整数构造函数,使它们生成字符串(主要是出于好奇,但为了暂时解决 LaTeX 的 \frac{}{} 不便提供一个良好的输入替代方案)。

我希望能够使用语言本身来完成这个任务,而不是使用特殊的解析器,但我猜这可能行不通...

module Main where

import Prelude hiding ((+))

newtype A = A Int deriving (Eq, Show, Num)
default (A)

(+) :: A -> (A -> String)
(A a) + (A b) = (show a) ++ " + " ++ (show b)

main2 = 3+4

main :: IO ()
main = putStrLn main2

上述代码的问题在于 + 函数只适用于 (A, A),而不适用于 (A, String) 等。如果简单地省略模式匹配 "(A a)" 并写入 "a",那么 show() 函数会在前面添加 "A ",所以 "3" 会变成 "A 3" 而不是仅仅是 "3"。
我想覆盖 A 的 Show,但这似乎是一个相当棘手的问题...
3个回答

16

如果您想要自己的 Show 实例用于 A,那么只需不派生它并创建自己的实例即可:

newtype A = A Int deriving (Eq, Num)

instance Show A where
  show (A a) = show a

那么你可以写类似以下的代码:

(+) :: (Show a, Show b) => a -> b -> String
a + b = show a ++ " + " ++ show b

当然,如果你像那样定义自己的+操作符,那么我认为你的问题不需要newtype A声明:

module Main where

import Prelude hiding ((+))

(+) :: (Show a, Show b) => a -> b -> String
a + b = show a ++ " + " ++ show b

aSum = 3 + 4

main :: IO ()
main = putStrLn aSum

谢谢,但它不是非常组合化:3 + 4 + 2会产生'"3 + 4" + 2'。对于我这样的新手感到抱歉...如果您知道解决此问题的方法,我将不胜感激!感谢指出主要问题——派生Show。 - gatoatigrado
好的 - 现在我想我明白你想做什么了。请看我的第二个答案。它将正确处理3 + 4 + 2。 - MtnViewMark
你可以使用 Monoid 类型类来实现 "+" 功能。 - Dalibor Filus

7
覆盖Haskell中默认的整数构造函数以生成字符串,可以通过为String定义Num实例来完成。 然后,(+)可以用作String-> String-> String。
一个超快速的示例:
{-# LANGUAGE TypeSynonymInstances #-}

module A where

instance Num String where (+) = (++)

{-

*A> "hello" + "world"
"helloworld"

-}

编写一个fromIntegral方法,将整数字面量转换为字符串函数(例如1 -->“1”)。

如果要更一般化、更有纪律性地将Num值列表提升到Num,请参阅Hinze方法将流作为Num:http://hackage.haskell.org/package/hinze-streams


这看起来像是一个不错的解决方案,但它会让ghci输出很多警告... fromIntegral函数怎么样?非常感谢! - gatoatigrado

5

您是否想创建一个数值类型,以便在Haskell中编写表达式,然后将其打印出来,并将其显示为LaTeX数学字符串?

module Main where

import Data.Ratio

data LaTeXmath = E Precedence String
    deriving (Eq)

data Precedence = Pterm | Pmul | Padd | Pexp
    deriving (Show, Eq, Ord, Bounded)

expr :: Precedence -> LaTeXmath -> String
expr p (E q s) | p >= q    = s
               | otherwise = "\\left(" ++ s ++ "\\right)"

instance Num LaTeXmath where
    a + b = E Padd (expr Padd a ++ " + " ++ expr Padd b)
    a - b = E Padd (expr Padd a ++ " - " ++ expr Padd b)
    a * b = E Pmul (expr Pmul a ++ " "   ++ expr Pmul b)

    negate a = E Pterm (" -" ++ expr Pterm a)
    abs    a = E Pterm (" |" ++ expr Pexp a ++ "| ")
    signum a = E Pterm (" \\signum (" ++ expr Pexp a ++ ") ")

    fromInteger i = E Pterm (show i)

instance Fractional LaTeXmath where
    a / b = E Pterm ("\\frac{" ++ expr Pexp a ++ "}{" ++ expr Pexp b ++ "}")

    fromRational r = fromInteger num / fromInteger denom
        where num = numerator r
              denom = denominator r

instance Show LaTeXmath where
    show a = "\\[" ++ expr Pexp a ++ "\\]"

sym :: String -> LaTeXmath
sym x = E Pterm x

anExample :: LaTeXmath
anExample = sym "y" / (recip 2 * ( 3 + sym "x" + 2 * sym "y" ) )

main :: IO ()
main = print anExample

这在处理优先级以正确插入括号所需的逻辑上变得复杂。示例打印出以下内容:
\[\frac{y}{\frac{1}{2} \left(3 + x + 2 y\right)}\]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接