Вопрос Хаскелла:ограничение типов данных для использования show
-
13-09-2019 - |
Вопрос
Код:
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 .