Вопрос на Haskell: ограничение типов данных для использования show - PullRequest
3 голосов
/ 07 декабря 2009

Код:

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

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

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

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

Ответы [ 5 ]

6 голосов
/ 07 декабря 2009
{-# 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
4 голосов
/ 07 декабря 2009

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

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

Но это почти всегда плохая идея, потому что она заставляет каждую функцию, которая использует Exp, упоминать ограничение show, даже если она никогда не использует методы Show. Вместо этого установите ограничение show только на те функции, для которых оно имеет значение. См. Real World Haskell для объяснения.

2 голосов
/ 08 декабря 2009

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

data Exp = Const String | Eq Exp Expr

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

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

1 голос
/ 09 декабря 2009

Я бы просто объявил ваш тип данных экземпляром класса type 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

Это бы сработало. Вы также можете использовать ГАДЦ:

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

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

1 голос
/ 07 декабря 2009

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

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

Будет ли это полезно для вас, зависит от того, что вы планируете делать с типом.

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

...