Hello fellow Haskell Fans!
All my questions are about the -- OVERLOADED(?) FUNCTION -- part, I included the rest for completeness.
I was wondering if it makes sense to use Pattern Matching to Overload my function order like I did in my example below.
I was also wondering if the first function with the function call "checkBalance balance" in the first version of the order function always gets executerd (because I didn't sepcify a pattern for it) or never (because all the patterns of Food are covered in the functions below).
Thanks in advance from a beginner :)
-- TYPE DECLARATIONS --
data Spice = Regular | Medium | Hot
data Base = Noodles | Rice
data Meat = Duck | Chicken | Pork
data Sauce = Tomato | Meatballs | Carbonara
data Food = Indian Spice | Pasta Sauce | Chinese Base Meat
data DeliveryOption = Pickup | Delivery
data DeliveryTime = Immediate | Later
type CreditBalance = Int
data Order = O Food DeliveryOption CreditBalance
data OrderStatus = Success | Pending | Declined
-- OVERLOADED(?) FUNCTION --
order :: (Order, CreditBalance) -> OrderStatus
order (O {}, balance)
| not (checkBalance balance ) = Declined
| ...
order (O Indian {} _ _, _)
| ...
order (O Pasta {} _ _, _)
| ...
order (O Chinese {} _ _, _)
| ...
-- ANOTHER FUNCTION --
checkBalance :: CreditBalance -> Bool
checkBalance balance
| balance > 100 = True
| otherwise = False
I can't see anything really wrong with that function definition.
Function clauses are tried in order, so that first branch with checkBalance will always be tried first, and the next guard, and so on, and if none of the guards of the first group is matched, then the next group will be tried (O Indian {} _ _).
If the guards of the first group were exhaustive, then the other branches below would not be reachable, which would mean something is wrong but it's hard to say more without more details.
-- OVERLOADED(?) FUNCTION --
order :: (Order, CreditBalance) -> OrderStatus
order (O {}, balance)
| not (checkBalance balance ) = Declined
| ...
The above pattern will cover every case and anything below it will never have the opportunity to check.
Reason is that Order has only one constructor, namely O, and (O {}), matches all possible arguments to the O constructor. The other member of the tuple is just a simple Int which always matches.
Since patterns are matched from top to bottom and the first that matches is chosen, the ordering of their definition in code is important. If you put the broadest possible pattern at the top, the more specific ones below will never have the opportunity to match.
As for overloading function, I can think of how one could (ab)use pattern matching to imitate function overloading as in OOP, but then you would also need to (ab)use data declarations and the whole type system to bend them to conform to such an idea and this would just make things worse.
Related
Let's say we have the following Haskell:
data T = T0 | T1 | T2 | ... | TN
toInt :: T -> Int
toInt t = case t of
T0 -> 0
T1 -> 1
T2 -> 2
...
TN -> N
What algorithm is used to perform the pattern match here? I see two options:
(1) Linear search, something like
if (t.tag == T0) { ... }
else if (t.tag == T1) { ... }
else ...
(2) Binary search, which would be sensible in this specific task: searching for t.tag in the set {TO...T1023}. However, where pattern matching in general has many other capabilities and generalizations, this may not be used.
Compiling with GHC, what algorithm is used, and what is the time complexity in terms of N, for pattern matching on t in toInt?
A jump table is used, making the pattern-match a constant time operation.
Unfortunately I'm unable to find an up-to-date citation for this, although this page mentions the implementation of Cmm-level switch statements as jump tables, and this old tagging design document uses a case on a Bool as an example, producing a jump table.
In Haskell it is possible to throw type errors during the type inference to enforce typing constraints on DSLs.
ie
class ValidHarmInterval (i :: IntervalType)
instance TypeError (Text "Minor seconds forbidden.")
=> ValidHarmInterval (Interval Min Second)
instance TypeError (Text "Major sevenths forbidden.")
=> ValidHarmInterval (Interval Maj Seventh)
instance {-# OVERLAPPABLE #-} ValidHarmInterval i
Is something similar to this possible in OCaml?
I do not know of an equivalent to TypeError in OCaml, and a quick search didn't turn up anything obvious. But I can think of two ways of achieving the effect you're seeking: enforcing type constraints on DSLs.
Typed final (tagless-final) style EDSLs
You may want to check out the "typed final" style of embedding DSLs descibred by Oleg. It seems to have precisely the properties you want:
The typed final approach is particularly attractive if the DSL to embed is also typed. We can then represent in the host language not only terms but also the type system (type derivations) of the DSL. Only well-typed DSL terms are embeddable.
The tutorial Modular, composable, typed optimizations in the tagless-final style gives step-by-step instruction on writing EDSLs in this style with OCaml.
Polymorphic Type Variants
OCaml offers another, more light weight (but perhaps less well behaved?) way to impose type constraints on subtyping relations of this sort: polymorphic variants.
We can define a type for intervals using polymorphic variants thus:
type 'a interval =
[< `Unison
| `Second
| `Third
| `Fourth
| `Fifth
| `Sixth
| `Seventh
| `Octave
] as 'a
where < indicates that any subset of the variants can construct a value of type 'a interval (ignore the type variable for the time being).
Thus, we can write a standard function that takes an _ interval to a string, and it will type check as _ interval -> string, as expected:
let interval_to_string : _ interval -> string = function
| `Unison -> "Unison"
| `Second -> "Second"
| `Third -> "Third"
| `Fourth -> "Fourth"
| `Fifth -> "Fifth"
| `Sixth -> "Sixth"
| `Seventh -> "Seventh"
| `Octave -> "Octave"
But we can also define a function that takes only some values of type _ interval:
let even_interval_to_int : _ interval -> int = function
| `Second -> 2
| `Fourth -> 4
| `Sixth -> 6
| `Octave -> 8
Like interval_to_string, even_interval_to_int is also a function of values of type _ interval, but type checking will fail if you apply it to an unsupported interval:
let invalid_int_of_even_interval = even_interval_to_int `Third
(* Error: This expression has type [> `Third ]
* but an expression was expected of type
* [< `Fourth | `Octave | `Second | `Sixth ]
* The second variant type does not allow tag(s) `Third *)
This is because [< Fourth |Octave | Second |Sixth ] is a subtype of _ interval.
Turning to your example (and please excuse my ignorance of music theory), we can encode our minor and major harmonic intervals as intersecting, but non-identical subsets of _ interval:
type major_harmonic_interval =
[ `Unison
| `Second
| `Third
| `Fourth
| `Fifth
| `Sixth
(* No Seventh *)
| `Octave
]
type minor_harmonic_interval =
[ `Unison
(* No Second*)
| `Third
| `Fourth
| `Fifth
| `Sixth
| `Seventh
| `Octave
]
Then constrain our type harmonic_interval, so that the Major and Minor constructors can only construct values with variants of the appropriate sort:
type harmonic_interval =
| Major of major_harmonic_interval
| Minor of minor_harmonic_interval
This will let us construct the harmonic intervals we want:
let major_second = Major `Second
but compel the type system to forbid any harmonic intervals we don't
let minor_second = Minor `Second
(* Error: This expression has type [> `Second ]
* but an expression was expected of type minor_harmonic_interval
* The second variant type does not allow tag(s) `Second *)
Meanwhile, we can still make use of the functions we've written that operate on values of type _ interval:
let harmonic_interval_to_string : harmonic_interval -> string = function
| Major interval -> "Major " ^ interval_to_string interval
| Minor interval -> "Minor " ^ interval_to_string interval
Sorry, this may seem similar to an earlier question but I am still a little confused. Here is the same code I am using as an example:
type Pig = String
type Lion = String
type Feed = [(Char,Char)]
type Visitors = [(Char,Char)]
type Costs = (Int,Int,Int)
data AnimalHome = Farm Pig Pig Pig Feed | Zoo Lion Lion Lion Feed Visitors
orders :: Char -> AnimalHome -> Costs -> Char
orders stuff (Farm p1 p2 p3 feed) (cost1,cost2,cost3) = some code here
But this time I want to execute different functions if the costs are different i.e. (1,3,9) executes a different equation to (0,0,16) etc.
You can adapt the answers to your previos similar questions for this case. Remember that a 3-tuple or triple (i.e. your type Costs) is just another data type. If there were no tuples in Haskell, you could write:
data Triple a b c = Triple a b c
and it would behave exactly as 3-tuples do! The only difference is that Hakell supports a more convenient syntax both for tuple expressions and tuple patterns.
Now for Patterns in general: Simplifying a bit, there are basicall 3 possibilities to write them:
a literal (suitable only for data types that support this, like String, Int, etc.), matches only that value.
a variable, matches any value
a data constructor, applied to patterns. Matches values constructed with that data constructor where the subpatterns match.
Turns out that you already wrote correct patterns in your question:
i.e. (1,3,9) executes a different equation to (0,0,16)
hence
orders stuff (Farm p1 p2 p3 feed) (1,3,9) = some code here
orders stuff (Farm p1 p2 p3 feed) (0,0,16) = some other code here
It probably would help us to help you if we could understand what your specific issues with pattern matching is. i.e. why you couldn't just try to come up with that yourself, as it feels quite natural, does it not?
If you really mean to use different functions with different integers, it's fine to use pattern matching
orders stuff (Farm p1 p2 p3 feed) (1,3,9) = -- one thing
orders stuff (Farm p1 p2 p3 feed) (0,0,16) = -- another thing
but I'd only really use that with 0, not other values. Really what you're after is called guards:
orders stuff (Farm p1 p2 p3 feed) (a,b,c)
| a == 0 && b == 0 = -- one thing
| a*b^2=c = -- another thing
| b < 0 = -- a third thing
and each condition is checked in turn - only the first true condition gets to run its code.
In Haskell, its straightforward to create a datatype for for a recursive tree, like what we have with XML documents:
data XML =
Text String -- Text of text node
| Elem String [XML] -- Tagname; Child nodes
and its related folds:
-- Simple fold (Child trees don't see the surrounding context.)
foldt :: (String -> a) -> (String -> [a] -> a) -> XML -> a
foldt fT fE (Text text) = fT text
foldt fT fE (Elem tagname ts) = fE tagname (map (foldt fT fE) ts)
-- Threaded fold for streaming applications.
-- See http://okmij.org/ftp/papers/XML-parsing.ps.gz
foldts :: (a -> String -> a) -> (a -> String -> a) -> (a -> a -> String -> a) -> a -> XML -> a
foldts fT fE_begin fE_end = go
where
go seed (Text text) = fT seed text
go seed (Elem tag children) =
let seed' = fE_begin seed tag in
let seed'' = foldl go seed' children in
fE_end seed seed'' tag
My problem now is that I don't know how to add some extra restrictions to my tree datatype in order to model HTML. In HTML each element node can only appear in the correct contexts and each element corresponds to a different context for its children. For example:
Void elements like img have an empty context model and are not allowed to have any children.
Elements with a Text content model, such as title, can only have text nodes as children (no nested tags allowed)
div elements cannot appear in a Phrasing context and therefore are not allowed to be descendants of span elements.
So my questions are:
What would I have to do to model these restrictions in Haskell98? (I ask this because I guess the Haskell98 model should translate better to other programming languages)
I imagine we might have to create lots of different datatypes for the different contexts but I don't know how to do this in a principled and clear manner. How can I do this without getting lost and what would the fold functions end up looking like?
What would the model look like if we are allowed to use modern GHC features such as GADTs?
I have a hunch GADTs might help push the restrictions into the typechecker, keeping the fold functions simple but I'm don't have much experience with them...
I don't need a 100% functioning solution, since that would obviously be beyond the scope of a Stack Overflow discussion. I just need enough to be able to get a better grasp of GADTs and things like and to be able to do the rest by myself.
This doesn't need GADTs (at least not yet). You just have to teach the compiler more information about your tree type.
data HTML
= HTML HTMLHeader HTMLBody
data HTMLHeader
= Header String
data HTMLBody
= Body [HTMLContent]
data HTMLContent
= Text HTMLText
| Title HTMLText
| Img String
| Elem String [HTML]
data HTMLText
= Literal String
| Bold String
| Italic String
| TextElems [HTMLText]
Now you get some invariants:
-- Must have header and body. titles can't contain images.
x = HTML
(Header "TEST") $ Body [
Title (Literal "Title")
,Text (Bold "Content")
]
A principled way to derive this tree would be to take it from a
particular HTML grammar - e.g. the XML EBNF perhaps - http://www.w3.org/TR/2006/REC-xml11-20060816/#sec-well-formed .
With GADTs some things can be encoded more efficiently, and you can write
functions over your data types that can enforce stronger invariants.
As you start making more and more properties statically verifiable, it may become more complex to encode the invariants. That's when GADTs, type families and other extensions can start to help.
This has been done by the OCsigen project,
a web framework implemented in
OCaml, that seeks to provide
strong typing guarantee.
You can have a look at their Html5 interface on this documentation
page. See for example the
type of the img smart
constructor
(it's a mouthful!):
val img :
src:Xml.uri ->
alt:Html5_types.text ->
([< `Accesskey
| `Class
| `Contenteditable
| `Contextmenu
| `Dir
| `Draggable
| `Height
| `Hidden
| `Id
| `Ismap
| `OnAbort
| `OnBlur
| `OnCanPlay
| `OnCanPlayThrough
| `OnChange
| `OnClick
| `OnContextMenu
| `OnDblClick
| `OnDrag
| `OnDragEnd
| `OnDragEnter
| `OnDragLeave
| `OnDragOver
| `OnDragStart
| `OnDrop
| `OnDurationChange
| `OnEmptied
| `OnEnded
| `OnError
| `OnFocus
| `OnFormChange
| `OnFormInput
| `OnInput
| `OnInvalid
| `OnKeyDown
| `OnKeyPress
| `OnKeyUp
| `OnLoad
| `OnLoadStart
| `OnLoadedData
| `OnLoadedMetaData
| `OnMouseDown
| `OnMouseMove
| `OnMouseOut
| `OnMouseOver
| `OnMouseUp
| `OnMouseWheel
| `OnPause
| `OnPlay
| `OnPlaying
| `OnProgress
| `OnRateChange
| `OnReadyStateChange
| `OnScroll
| `OnSeeked
| `OnSeeking
| `OnSelect
| `OnShow
| `OnStalled
| `OnSubmit
| `OnSuspend
| `OnTimeUpdate
| `OnVolumeChange
| `OnWaiting
| `Spellcheck
| `Style_Attr
| `Tabindex
| `Title
| `User_data
| `Width
| `XML_lang
| `XMLns ],
[> `Img ])
nullary
(In OCaml's standard syntax, a type t with three type parameters
a, b and c is written (a,b,c) t rather than t a b c).
The fact that <img> may have no child is encoded by the use of the
"nullary" type here. The rest of the static information encodes which
kinds of attributes may be used on this node.
The weird `Foo | `Bar | `Baz stuff is a so-called "polymorphic
variant" (presented in eg. this article), a kind of extensible structural sum type using row
polymorphism that is more or less unique to OCaml (while they would be
useful to any programming language, as extensible records generalize
the usual nominal records of OCaml and Haskell). Here there are mostly used as a form of type-level lists.
Other than that, that is a relatively classic use of phantom types, only lead to extreme sizes because of the sheer number of cases you have in the HTML spec. Phantom types are the precursors of GADT to enforce additional type
abstraction in the ML world. In Haskell98, you would probably try to
encode the same kind of type-level information using type-classes
rather than directly type abstractions.
I would like to use haskell to implement a game, and would like to use a system of type classes to implement the item system. It would work something like this:
data Wood = Wood Int
instance Item Wood where
image a = "wood.png"
displayName a = "Wood"
instance Flammable Wood where
burn (Wood health) | health' <= 0 = Ash
| otherwise = Wood health'
where health' = health - 100
where the Item and Flammable classes are something like this:
class Item a where
image :: a -> String
displayName :: a -> String
class Flammable a where
burn :: (Item b) => a -> b
To do this, I would need a way to detect whether a value is an instance of a type class.
The Data.Data module gives a similar functionality so that leads me to believe that this is possible.
Type classes are probably the wrong way to go here. Consider using plain algebraic data types instead, for example:
data Item = Wood Int | Ash
image Wood = "wood.png"
image Ash = ...
displayName Wood = "Wood"
displayName Ash = "Ash"
burn :: Item -> Maybe Item
burn (Wood health) | health' <= 0 = Just Ash
| otherwise = Just (Wood health')
where health' = health - 100
burn _ = Nothing -- Not flammable
If this makes it too hard to add new items, you can instead encode the operations in the data type itself.
data Item = Item { image :: String, displayName :: String, burn :: Maybe Item }
ash :: Item
ash = Item { image = "...", displayName = "Ash", burn :: Nothing }
wood :: Int -> Item
wood health = Item { image = "wood.png", displayName = "Wood", burn = Just burned }
where burned | health' <= 0 = ash
| otherwise = wood health'
health' = health - 100
However, this makes it harder to add new functions. The problem of doing both at the same time is known as the expression problem. There is a nice lecture on Channel 9 by Dr. Ralf Lämmel where he explains this problem more in depth and discusses various non-solutions, well worth the watch if you have time.
There are approaches to solving it, but they are considerably more complex than the two designs I've illustrated, so I recommend using one of those if it fits your needs, and not worrying about the expression problem unless you have to.
Here's the problem:
burn :: (Item b) => a -> b
What this means is that the result value of burn must be polymorphic. It must be able to fill any hole for any instance of Item.
Now, it's quite apparent you're trying to write something like this (in imaginary OO language with interfaces and subclassing):
Interface Item {
String getImage();
String getDisplayName();
}
Interface Flammable {
Item burn();
}
In this sort of code, you're saying that burn will produce some item, without any guarantees about what kind of item it is. This is the difference between "for all" and "there exists". What you wanted to express in the Haskell code was "there exists", but what you actually expressed was "for all".
Now if you're really sure you want to do "there exists" functionality, you can take a look at using Existential Types. But beware. If you are planning on writing code like this:
if (foo instanceof Flammable) {
...
}
Then you are almost certainly doing it wrong, and will run into much pain and agony. Instead consider hammar's suggested alternatives.