Вопрос Хаскелла:ограничение типов данных для использования show

StackOverflow https://stackoverflow.com/questions/1857131

Вопрос

Код:

data Exp a = Const a | Eq (Exp a) (Exp a)

Я хочу, чтобы Const а содержать значение типа show, чтобы я мог распечатать его позже.Итак, в C # я бы написал:

class Const : Exp { IShow X; }
class Eq : Exp { Exp X, Y; }

Как я могу это сделать в Haskell?

Это было полезно?

Решение

{-# LANGUAGE GADTs #-}

data Exp a where
    Const :: Show a => a -> Exp a
    Eq :: Exp a -> Exp a -> Exp a

Если вы хотите разрешить использование различных типов данных в разных ветвях Eq это тоже прекрасно.

data Exp where
    Const :: Show a => a -> Exp
    Eq :: Exp -> Exp -> Exp

Другие советы

Вы можете сделать это, сказав

data (Show a) => Exp a = Const a | Eq (Exp a) (Exp a)

Но это почти всегда плохая идея, потому что она вынуждает каждую функцию, использующую Exp упомянуть ограничение show, даже если оно никогда не использует Show методы.Вместо этого установите ограничение show только для тех функций, к которым оно относится.Видишь Хаскелл из Реального Мира для объяснения.

Если все, что вы хотите знать о аргументе, чтобы Const это то, что ты можешь show это, почему бы просто не сохранить полученный String значение в конструкторе вместо этого?Например:

data Exp = Const String | Eq Exp Expr

example = Eq (Const (show 0)) (Const (show ""))

Это очень похоже на вашу версию C #.

Чтобы ответить на второй вопрос, заданный в комментариях, Eq (Const 0) (Const "") это недостижимо с тем типом данных, который у вас есть, потому что Exp Integer и Exp String это не один и тот же тип.Один из вариантов - сделать что-то вроде

data Exp = forall a . Show a => Const a | Eq Exp Exp

Принесет ли это вам какую-либо пользу, зависит от того, что вы планируете делать с этим типом.

Редактировать:Для этого требуется включить языковые расширения, как уже упоминалось.

Я бы просто объявил ваш тип данных экземпляром типа class Show:

data Exp a = Const a | Eq (Exp a) (Exp a)

instance (Show a) => Show (Exp a) where
    show (Const a) = show a
    show (Eq x y ) = "[ " ++ show x ++ " , " ++ show y ++ " ]"

Посмотрите, что происходит, когда вы загружаете это в ghci и делаете:

*Main> let x = Eq (Const 1) (Eq (Const 2) (Const 3))
*Main> x      
[1 , [2 , 3] ]

Отвечающий комментарий:

Вы можете легко иметь дело с различными типами.Предположим, вы хотите проанализировать математические выражения.Вы можете иметь следующую структуру, например:

data Expr  = Var String | Sum (Expr) (Expr) | Number Int | Prod (Expr) (Expr)

Этого достаточно для представления любого выражения, состоящего из сумм и произведений чисел и именованных переменных.Например:

x = Sum (Var "x") (Prod (Number 5) (Var "y")) 

представляет:x + 5y

Чтобы напечатать это красиво, я бы сделал:

instance Show Expr where
    show (Var s) = show s
    show (Sum x y) = (show x) ++ " + " (show y)
    show (Prod x y) = (Show x) ++ (show y)
    show (Number x) = show x

Это сделало бы свое дело.Вы также могли бы использовать GADTs:

 data Expr where
      Var :: String -> Expr
      Sum :: Expr -> Expr -> Expr

и т.д...а затем создайте экземпляр этого как Show .

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top