Haskell data type - haskell

Let's say that I have a type like this (that works):
data WAE = Num Float
| Id String
| With [(String, WAE)] WAE -- This works, but I want to define it as below
deriving(Eq, Read, Show)
I want a type like this (doesn't work):
data WAE = Num Float
| Id String
| With [(Id, WAE)] WAE -- This doesn't work ("Id not in scope")
deriving(Eq, Read, Show)
Why can't I do that in Haskell? Any ideas in achieveing a similar effect?

In Haskell, there are two distinct namespaces. One for values, and one for types. Data constructors such as Id live in the value namespace, whereas type constructors such as String, as well as classes live in the type namespace. This is OK because there is no context where both would be allowed.
In the definition of a data type, the two namespaces live side by side, as you're defining a both a new type constructor and several new data constructors, while referring to existing type constructors.
data WAE = Num Float
| Id String
| With [(String, WAE)] WAE
deriving(Eq, Read, Show)
Here, WAE, Float, String, (,), [], Eq, Read and Show are all names in the world of types, whereas Num, Id and With are names in the world of values. Mixing them does not make any sense, and this is why Id is not in scope in your second piece of code, as you are in a type context, and there is no type-level thing called Id.
It's not 100% clear from your question what you were trying to do, but I suspect it might be something like this:
type Id = String -- Or perhaps a newtype
data WAE = Num Float
| Id Id
| With [(Id, WAE)] WAE
deriving(Eq, Read, Show)
Note that because the namespaces are distinct, it's perfectly fine to have something called Id in both.

Based on your question, I'm not entirely sure what you are trying to accomplish. Here are two possibilities that are possible with Haskell's type-system.
data WAE = Num Float
| Id String
| With [(WAE, WAE)] WAE
deriving (Eq, Read, Show)
I'm not sure this is what you want, however, because it allows for more than just an id in the first portion of the pair.
Another possibility is to create a new type (or alias) for the id, like so:
data MyId = MyId String deriving (Eq, Read, Show)
data WAE = Num MyId Float
| Id MyId
| With [(MyId, WAE)] WAE
deriving (Eq, Read, Show)
Note that MyId could also be created with either newtype or type, each giving a slightly different meaning.
You cannot, however use Id as it is a data-constructor, where a type is expected.

Here:
| With [(Id, WAE)] WAE
you are using a data constructor Id in a context where a type is expected. The type of Id is WAE, so if you change this line to:
| With [(WAE, WAE)] WAE
it will compile, but the result is probably not what you want.
If you just want to show that the string in question is an identifier semantically, you can use a type alias:
type Id = String
data WAE = Num Float
| Id Id
| With [(Id, WAE)] WAE
deriving(Eq, Read, Show)

Related

Haskell - Restrict data parameters

I am trying to create the following datatypes:
data ExprValues = ExprNum Int |
ExprIdent String |
ExprChar Char |
ExprString String
deriving(Eq, Show, Read)
data ExprOverall = ExprFunCall ExprValues --correct this to use ExprIdent solely
deriving(Eq, Show, Read)
However, as it is indicated in the comment, I want that ExprValues next to the ExprFuncall to accept ExprIdent String only, not the other ExprValues. How am I able to do it?
First off, if you want to allow only the constructor, why not just “inline” it – store a String right in ExprOverall and be done?
But more generally, this sort of restriction can be implemented with GADTs. Often, especially for such AST-like types, you want to express the overall type the expression stands for. There, ExprIdent might be polymorphic while the others are concrete:
{-# LANGUAGE GADTs #-}
data ExprValues a where
ExprNum :: Int -> ExprValues Int
ExprIdent :: String -> ExprValues a
ExprChar :: Char -> ExprValues Char
ExprString :: String -> ExprValues String
Then, for use in ExprOverall you select a unique tag-type that is only applicable to the ExprIdent constructor (because that allows any type variable a, whereas the others are specific to a concrete type).
data FreeIdentifier
data ExprOverall = ExprFunCall (ExprValues FreeIdentifier)

Writing an interpreter for an imperative language in Haskell

I am trying to build an interpreter for a C-like language in Haskell. I have so far written and combined small monadic parsers following this paper, hence so far I can generate an AST representation of a program. I defined the abstract syntax as follows:
data LangType = TypeReal | TypeInt | TypeBool | TypeString deriving (Show)
type Id = String
data AddOp = Plus | Minus | Or deriving (Show)
data RelOp = LT | GT | LTE | GTE | NEq | Eq deriving (Show)
data MultOp = Mult | Div | And deriving (Show)
data UnOp = UnMinus | UnNot deriving (Show)
data BinOp = Rel RelOp | Mul MultOp | Add AddOp deriving (Show)
data AST = Program [Statement] deriving (Show)
data Block = StatsBlock [Statement] deriving (Show)
data Statement = VariableDecl Id LangType Expression
| Assignment Id Expression
| PrintStatement Expression
| IfStatement Expression Block Block
| WhileStatement Expression Block
| ReturnStatement Expression
| FunctionDecl Id LangType FormalParams Block
| BlockStatement Block
deriving (Show)
data Expression = RealLiteral Double
| IntLiteral Int
| BoolLiteral Bool
| StringLiteral String
| Unary UnOp Expression
| Binary BinOp Expression Expression
| FuncCall Id [Expression]
| Var Id
deriving (Show)
data FormalParams = IdentifierType [(Id, LangType)] deriving (Show)
I have yet to type-check my AST and build the interpreter to evaluate expressions and execute statements. My questions are the following:
Does the abstract syntax make sense/can it be improved? In particular, I've been running into a recurring problem. In the EBNF of this language I'm trying to interpret, a WhileStatement consists of an Expression (which I have no problem with) and a Block, which in the EBNF happens to be a Statement just like WhileStatement, and so I cannot refer to Block from my WhileStatement. I've worked around this by defining a separate data type Block (as is shown in the above code), but am not sure if this is the best way. I'm finding defining data types quite confusing.
Since I have to type-check my AST and evaluate/execute, do I implement these separately or can I define some function which does them both at the same time?
Any general tips on how I should go about type-checking and interpreting the language would also be greatly appreciated. Since the language has variable and function declarations, I am thinking of implementing some sort of symbol table, although again I am struggling with defining the type for this. So far I've tried
import qualified Data.Map as M
data Value = RealLit Double | IntLit Int | BoolLit Bool | StringLit String | Func [FormalParams] String
deriving (Show)
type TermEnv = M.Map String Value
but I'm unsure whether I should be using my LangType from before.
Addressing your question in the comments about how to proceed with type checking and evaluation.
If you don't have to do inference or polymorphism, type checking is pretty simple. Also type checking and evaluation mirror each other pretty closely in these conditions.
Begin by defining a monad with the features you need. For a type checker, you will need
A type environment, i.e. a Reader(Map Id LangType) component, to keep track of the types of local variables.
An error ability, e.g. ExceptString.
So you could define a monad like
type TypeEnv = Map.Map Id LangType
type TC = ReaderT TypeEnv (Except String)
And then your typechecker function would look like:
typeCheck :: AST -> TC ()
(We return () because there is nothing interesting to be gained from the typechecking process besides knowing whether the program passed.)
This will be largely structurally inductive, e.g.
typeCheck (Program stmt) = -- typecheckStmt each statement*
typeCheckStmt :: Statement -> TC ()
typeCheckStmt (VariableDecl v type defn) = ...
typeCheckStmt (Assignment v exp) = do
Just t <- asks (Map.lookup v)
t' <- typeCheckExp exp
when (t /= t') $ throwError "Types do not match"
...
-- Return the type of a composite expression to use elsewhere
typeCheckExp :: Expression -> TC LangType
...
There will be a bit of finesse required to make sure that variable declarations in a list of statements can be seen by later statements in the same list. I will leave that as a puzzle. (Hint: see the local function to provide an updated environment within a scope.)
Evaluation is a similar story. You're correct that you need a type of run-time values. Without some cleverness that you are probably not ready for (and is of questionable utility even if you were) there is not really a way to use LangType in Value, so you're on the right track.
You will need a monad that supports keeping track of the values of variables and the ability to do whatever else your language needs. To start I recommend
type Eval = StateT (Map Id Value) IO
and proceed structurally as before. There will again be some finesse required when handling variable scopes and shadowing, and you may need to change the environment type or mess with your Value type a bit to accommodate these subtleties, but thinking through these problems is important. Start simple, don't try to implement typechecking and evaluation for your whole language at once.

Haskell : recursive data type (Parameterized Types)

I have this :
data Val s i a = S s | I i | A a deriving (Show)
To play with non-homogenous lists in Haskell. So I can do something like (just an example function ):
oneDown :: Val String Int [String]-> Either String String
But I would actually like this to be a list of Vals, i.e. something like :
oneDown :: Val String Int [Val]-> Either String String
What you're looking for would result in an infinite data type, which Haskell explicitly disallows. However, we can hide this infinity behind a newtype and the compiler won't complain.
data Val s i a = S s | I i | A a deriving (Show)
newtype Val' = Val' (Val String Int [Val']) deriving (Show)
It's still doing exactly what your example did (plus a few type constructors that will get optimized away at runtime), but now we can infinitely recurse because we've guarded the recursive type.
This is actually what the recursion-schemes library does to get inductively-defined data that we can define generic recursion techniques on. If you're interested in generalized data types like this, you may have a look at that library.
To construct this newly-made type, we have to use the Val' constructor.
let myVal = A [Val' (I 3), Val' (S "ABC"), Val' (A [])]

A defined data type not defined?

I need some help trying to understand why these definitions
data SegmentList
= SegmentList SegmentlistHeader [Segment]
| AugmentedSegmentList SegmentlistHeader [AugmentedSegment]
deriving (Show)
data SegmentlistHeader
= SegmentlistHeader DatabaseName Query LabelType TimeStamp
deriving (Show)
data Segment
= Segment SegmentLabel SegmentStart SegmentEnd Session Checksum
| AugmentedSegment SegmentLabel SegmentStart SegmentEnd Session Checksum Metadata
deriving (Show)
type DatabaseName = String
type SegmentLabel = String
type SegmentStart = Double
type SegmentEnd = Double
type Session = String
type LabelType = String
type Query = String
type TimeStamp = String
type Checksum = String
type Metadata = [(String, String)]
result in this error message:
Not in scope: type constructor or class `AugmentedSegment'
A data constructor of that name is in scope; did you mean -XDataKinds?
while this :
data UmeQueryPart
= LabelInLabelType String [String] String
| LabelType String
| UmeQueryDominance UmeQueryPart UmeQueryPart String
| UmeQuerySequence UmeQueryPart UmeQueryPart String
| UmeQueryIntersect [UmeQueryPart]
| UmeQueryUnion [UmeQueryPart]
deriving Show
compiles like a charm. I'm obviously not skilled enough to see the difference...
From your edit, you have
data SegmentList
= SegmentList SegmentlistHeader [Segment]
| AugmentedSegmentList SegmentlistHeader [AugmentedSegment]
deriving (Show)
data Segment
= Segment SegmentLabel SegmentStart SegmentEnd Session Checksum
| AugmentedSegment SegmentLabel SegmentStart SegmentEnd Session Checksum Metadata
deriving (Show)
The problem specifically is from the second constructor of SegmentList:
AugmentedSegmentList SegmentlistHeader [AugmentedSegment]
Here you've said that AugmentedSegmentList contains a list of AugmentedSegment, but AugmentedSegment is a constructor of Segment, it is not a type. When defining a data type, it can only reference other types, not other constructors. You could change it to
AugmentedSegmentList SegmentlistHeader [Segment]
But this probably isn't exactly what you want. Since your Segment and AugmentedSegment constructors are identical other than the addition of the Metadata field, I would instead recommend having
data Segment
= Segment SegmentLabel SegmentStart SegmentEnd Session Checksum
deriving (Show)
data AugmentedSegment
= AugmentedSegment Segment Metadata
deriving (Show)
Then you can have a list of only AugmentedSegments which is enforced by the type system, but it also means that you can't pass in an AugmentedSegment to a function that accepts a Segment. Since these appear to be different things, this shouldn't be a problem. If it is, you can always just pass in the contained Segment field from an AugmentedSegment into the function you're concerned with, or you can alter your function to accept a value of type Either Segment AugmentedSegment instead.
In summary, when you have something like
data MyType = MyConstructor FieldA FieldB FieldC
The MyType symbol is the name of the type, the MyConstructor symbol is a constructor, you can think of it as a function that returns something of type MyType, and the FieldNs symbols all have to be existing types or can recursively refer to MyType, they can not be other constructors. This is what caused the error you're seeing.

Record syntax default value for accessor

As I was writing up an answer just now, I ran across an interesting problem:
data Gender = Male | Female
deriving (Eq, Show)
data Age = Baby | Child | PreTeen | Adult
deriving (Eq, Show, Ord)
data Clothing = Pants Gender Age
| Shirt Gender Age
| Skirt Age -- assumed to be Female
deriving (Show, Eq)
Suppose I wish to write the final data type with record syntax:
data Clothing = Pants {gender :: Gender, age :: Age}
| Shirt {gender :: Gender, age :: Age}
| Skirt {age :: Age}
deriving (Show, Eq)
The problem is, I want gender $ Skirt foo to always evaluate to Female (regardless of foo, which is an Age). I can think of a few ways to accomplish this, but they require that I either
use smart constructors, theoretically allowing Skirt Male foo but not exposing Constructors
define my own gender function
With #1, by not exposing the constructor in the module, I effectively prevent users of the module from taking advantage of record syntax. With #2, I have to forego record syntax entirely, or define an additional function gender', which again defeats record syntax.
Is there a way to both take advantage of record syntax, and also provide a "default", unchangeable value for one of my constructors? I am open to non-record-syntax solutions as well (lenses, perhaps?) as long as they are just as elegant (or moreso).
Is there a way to both take advantage of record syntax, and also provide a "default", unchangeable value for one of my constructors?
In the absence of a convincing counterexample, the answer seems to be "no".
Yes there is a tension between types and data... which by the way shows how thin is the line.
The pratical answer is to use a default instance as indicated in the Haskell Wiki. It does answer your exact question since you must give up direct constructor use.
Thus for your example,
data Age = Baby | Child | PreTeen | Adult | NoAge
data Clothing = Pants {gender :: Gender, age :: Age}
| Shirt {gender :: Gender, age :: Age}
| Skirt {gender :: Gender, age :: Age}
deriving (Show, Eq)
skirt = Skirt { gender=Female, age=NoAge }
then developpers can create new instances with default values, using the copy-and-update facility of the record syntax
newSkirt = skirt { age=Adult }
and gender newSkirt evaluates to Female
I want to stress that this approach leads you to define default values at the type level, which I think is a Good Thing (of course the NoAge constructor is the Nothing of a Maybe Age type).

Resources