I have a question about recursive data structures in Haskell (language that I'm currently trying to learn).
I would like to encode in Haskell Prolog-like terms, but every solution I came up with has different drawbacks that I would really like to avoid. I would like to find a cheap and elegant way of encoding a BNF grammar in Haskell types, if you wish to see my problem from this perspective.
Just as a reminder, some prolog terms could be male, sum(2, 3.1, 5.1), btree(btree(0, 1), Variable).
Solution 1
data Term = SConst String
| IConst Integer
| FConst Double
| Var String
| Predicate {predName :: String, predArgs :: [Term]}
With this solution I can have nested predicates (since predArgs are Term), but I can't distinguish predicates from other terms in type signatures.
Solution 2
data Term = SConst String
| IConst Integer
| FConst Double
| Var String
data Predicate = Predicate {predName :: String, predArgs ::[Either Term Predicate}
In this variant I can clearly distinguish predicates from basic terms, but the Either type in the predArgs list can be quite a nuisance to manage later in the code (I think... I'm new to Haskell).
Solution 3
data Term = SConst String
| IConst Integer
| FConst Double
| Var String
| Struct String [Term]
data Predicate = Predicate String [Term]
With this last solution, I split terms in two different types as before, but this time I avoid Either Term Predicate adding a Struct constructor in Term with basically the same semantics as Predicate.
It's just like solution 1 with two predicate constructors for terms. One is recursion-enabled, Struct, and the other one, Predicate is to be able to distinguish between predicates and regular terms.
The problem with this try is that Struct and Predicate are structurally equivalent and have almost the same meaning, but I will not be able to write functions that works - in example - both on (Predicate "p" []) and (Struct "p" []).
So again my question is: please, is there a better way to encode my predicates and terms such that:
I'm able to distinguish between predicate and terms in type signatures;
nested predicates like p(q(1), r(q(3), q(4))) are supported;
I can write functions that will work uniformly on predicates, without any
distinction like the one in solution #3?
Please feel free to ask me for further clarifications should you need any.
Thank you very much.
You could add a term constructor to wrap a predicate. Here, I also factored all of the literals into their own data type:
data Term = TLit Literal
| TVar String
| TPred Predicate
data Literal = LitS String
| LitI Int
| LitF Double
data Predicate = Predicate String [Term]
Here's one way (that's probably not worth the trouble):
{-# LANGUAGE EmptyDataDecls #-}
-- 'T' and 'F' are short for 'True' and 'False'
data T = T
data F
-- 'p' is short for 'mayNotBeAPredicate'
data Term p
= SConst !p String
| IConst !p Integer
| FConst !p Double
| Var !p String
| Predicate {predName :: String, predArgs :: [Term T]}
sconst :: String -> Term T
iconst :: Integer -> Term T
fconst :: Double -> Term T
var :: String -> Term T
predicate :: String -> [Term T] -> Term p
sconst = SConst T
iconst = IConst T
fconst = FConst T
var = Var T
predicate = Predicate
checkPredicate :: Term p -> Maybe (Term F)
checkPredicate (Predicate name args) = Just (Predicate name args)
checkPredicate _ = Nothing
forgetPredicate :: Term p -> Term T
forgetPredicate (SConst _ s) = sconst s
forgetPredicate (IConst _ i) = iconst i
forgetPredicate (FConst _ f) = fconst f
forgetPredicate (Var _ s) = var s
forgetPredicate (Predicate name args) = predicate name args
You can now write functions which only accept predicates by giving them an input type of Term F, and functions which accept any input type by giving them an input type of Term p.
Related
Hi I'm trying to write a proof checker in Haskell. One way of representing terms is as follows:
data Term = Const String | Fun String [Term] | Var String
But this seems unideal, since String can take on many values and there is no way of verifying the arity of function terms.
One solution is as follows:
data Term a = Const String | Fun a | Var String
data NTFun = S (Term NTFun) | Plus (Term NTFun) (Term NTFun)
type NTTerm = Term NTFun
Now I have a more generic Term type and can instantiate it as I please. But if I want to abstract out constant symbols as well it gets messy.
data Term a c = Const c | Fun a | Var String
data NTFun c = S (Term c (NTFun c)) | Plus (Term c (NTFun c)) (Term c (NTFun c))
data NTConst = Zero
type NTTerm = Term (NTFun NTConst) NTConst
It seems there must be a better way to express this? Thanks!
You can change NTFun to be a functor, abstracting out its mentions of Term and any of its other variables, and have Term itself tie the knot.
data Term f c = Const c | Fun (f (Term f c)) | Var String
data NTFun term = S term | Plus term term
data NTConst = Zero
type NTTerm = Term NTFun NTConst
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 [])]
I'm looking for a way to simplify function patterns when the actual data is not required:
data X = A | B String | C Int Int String
myfn :: X -> Int
myfn A = 50
myfn (B _) = 200
myfn (C _ _ _) = 500
Is there a way to make a simpler pattern for matching C, just discarding the values?
hsdev adds a hint "Hint: use record patterns", but Google did not help me there.
You can use record patterns like this:
data X = A | B {name :: String} | C {x::Int, y::Int, name::String}
myfn :: X -> Int
myfn A = 50
myfn B{} = 200
myfn C{} = 500
Record patterns allow you to give names to the fields of the constructors.
you can also do things like:
myfn C{name=n} = length n
so you can see that you can pattern match only on the specific field you need.
Note: you can use the empty record pattern even with data types that do not use record syntax:
data A = A Int | B Int Int
myfn A{} = 1
myfn B{} = 2
This is fine.
There a number of other extensions related to record patterns:
RecordWildCards allows you to write things like C{..} which is equivalent to the pattern: C{x=x, y=y, name=name}, i.e. it matches all fields and you now have in scope x with the value matched for the x field etc.
NamedFieldPuns allows you to write C{name} to be equivalent to C{name=name}, so that name is now in scope and contains the value matched for the name field.
Keep in mind that using record patterns doesn't prevent you from using your constructors in a positional way, so you can still write:
myfn (B _) = 200
It only adds functionality.
I'm following this introduction to Haskell, and this particular place (user defined types 2.2) I'm finding particularly obscure. To the point, I don't even understand what part of it is code, and what part is the thoughts of the author. (What is Pt - it is never defined anywhere?). Needless to say, I can't execute / compile it.
As an example that would make it easier for me to understand, I wanted to define a type, which is a pair of an Integer and a String, or a String and an Integer, but nothing else.
The theoretical function that would use it would look like so:
combine :: StringIntPair -> String
combine a b = (show a) ++ b
combine a b = a ++ (show b)
If you need a working code, that does the same, here's CL code for doing it:
(defgeneric combine (a b)
(:documentation "Combines strings and integers"))
(defmethod combine ((a string) (b integer))
(concatenate 'string a (write-to-string b)))
(defmethod combine ((a integer) (b string))
(concatenate 'string (write-to-string a) b))
(combine 100 "500")
Here's one way to define the datatype:
data StringIntPair = StringInt String Int |
IntString Int String
deriving (Show, Eq, Ord)
Note that I've defined two constructors for type StringIntPair, and they are StringInt and IntString.
Now in the definition of combine:
combine :: StringIntPair -> String
combine (StringInt s i) = s ++ (show i)
combine (IntString i s) = (show i) ++ s
I'm using pattern matching to match the constructors and select the correct behavior.
Here are some examples of usage:
*Main> let y = StringInt "abc" 123
*Main> let z = IntString 789 "a string"
*Main> combine y
"abc123"
*Main> combine z
"789a string"
*Main> :t y
y :: StringIntPair
*Main> :t z
z :: StringIntPair
A few things to note about the examples:
StringIntPair is a type; doing :t <expression> in the interpreter shows the type of an expression
StringInt and IntString are constructors of the same type
the vertical bar (|) separates constructors
a well-written function should match each constructor of its argument's types; that's why I've written combine with two patterns, one for each constructor
data StringIntPair = StringInt String Int
| IntString Int String
combine :: StringIntPair -> String
combine (StringInt s i) = s ++ (show i)
combine (IntString i s) = (show i) ++ s
So it can be used like that:
> combine $ StringInt "asdf" 3
"asdf3"
> combine $ IntString 4 "fasdf"
"4fasdf"
Since Haskell is strongly typed, you always know what type a variable has. Additionally, you will never know more. For instance, consider the function length that calculates the length of a list. It has the type:
length :: [a] -> Int
That is, it takes a list of arbitrary a (although all elements have the same type) and returns and Int. The function may never look inside one of the lists node and inspect what is stored in there, since it hasn't and can't get any informations about what type that stuff stored has. This makes Haskell pretty efficient, since, as opposed to typical OOP languages such as Java, no type information has to be stored at runtime.
To make it possible to have different types of variables in one parameter, one can use an Algebraic Data Type (ADT). One, that stores either a String and an Int or an Int and a String can be defined as:
data StringIntPair = StringInt String Int
| IntString Int String
You can find out about which of the two is taken by pattern matching on the parameter. (Notice that you have only one, since both the string and the in are encapsulated in an ADT):
combine :: StringIntPair -> String
combine (StringInt str int) = str ++ show int
combine (IntString int str) = show int ++ str
I have written the following code to remove vowels from a sentence:
main = print $ unixname "The House"
vowel x = elem x "aeiouAEIOU"
unixname :: [Char] -> [Char]
unixname [] = []
unixname (x:xs) | vowel x = unixname xs
| otherwise = x : unixname xs
Just wondering if it is possible to create a data type for vowel? The compiler won't let me use characters in a data type.
Not directly. The problem is that characters are a built-in type with no facility for polymorphism. This is different from numeric literals, which are designed to be polymorphic via the Num type class.
That said, there are two basic approaches you can take: a newtype wrapper with a smart constructor, or a totally new type.
The newtype wrapper is easier to use:
module Vowel (Vowel, vowel, fromVowel) where
newtype Vowel = Vowel Char
vowel :: Char -> Maybe (Vowel)
vowel x | x `elem` "aeiouAEIOU" = Just (Vowel x)
| otherwise = Nothing
fromVowel :: Vowel -> Char
fromVowel (Vowel x) = x
Since the Vowel constructor isn't exported, new Vowels can only be created by the vowel function, which only admits the characters you want.
You could also make a new type like this:
data Vowel = A | E | I | O | U | Aa | Ee | Ii | Oo | Uu
fromChar :: Char -> Maybe Vowel
fromChar 'a' = Just Aa
fromChar 'A' = Just A
-- etc.
toChar :: Vowel -> Char
toChar Aa = 'a'
toChar A = 'A'
This second way is pretty heavyweight, and therefore is much more awkward to use.
So that's how to do it. I'm not quite certain that you want to though. The usual idiom is to make types that represent your data, and you specifically don't represent vowels. A common pattern would be something like this:
newtype CleanString = Cleaned { raw :: String }
-- user input needs to be sanitized
cleanString :: String -> CleanString
Here the newtype differentiates between unsanitized and sanitized input. If the only way to make a CleanString is by cleanString, then you know statically that every CleanString is properly sanitized (provided that cleanString is correct). In your case, it seems you actually need a type for consonants, not vowels.
Newtypes in Haskell are very lightweight*, but the programmer does have to write and use code to do the wrapping and unwrapping. In many instances the benefits outweigh the extra work. However, I really can't think of any application where it's important to know that your String is vowel-free, so I'd probably just work with a plain String.
*newtypes only exist at compile-time, so in theory there's no runtime performance cost to using them. However, their existence can change the produced code (e.g. inhibiting RULEs), so sometimes there is a measurable performance impact.
You could use phantom types to tag characters with extra information, in order to make the type system guarantee during compile time that your strings only contain, for example, vowels or non-vowels.
Here's a toy example:
{-# LANGUAGE EmptyDataDecls #-}
import Data.Maybe
newtype TaggedChar a = TaggedChar { fromTaggedChar :: Char }
data Vowel
data NonVowel
isVowel x = x `elem` "aeiouyAEIOUY"
toVowel :: Char -> Maybe (TaggedChar Vowel)
toVowel x
| isVowel x = Just $ TaggedChar x
| otherwise = Nothing
toNonVowel :: Char -> Maybe (TaggedChar NonVowel)
toNonVowel x
| isVowel x = Nothing
| otherwise = Just $ TaggedChar x
unixname :: [Char] -> [TaggedChar NonVowel]
unixname = mapMaybe toNonVowel
The benefit of this approach is that you can still also write functions that work on all TaggedChars regardless of the tag. For example:
toString :: [TaggedChar a] -> String
toString = map fromTaggedChar