The difference between algebraic data types and subclasses in data Bool = False | True - haskell

I am not really familiar with Haskell but am looking through some of it. I noticed this:
data Bool = False | True
In an OO language this could be done sort of using subclasses:
class Bool
class False < Bool
class True < Bool
Wondering at a high level what the difference is between these two constructs. Wondering if a simple algebraic data type can be considered a class and its subclasses. If not, why not.

It makes more sense to think of sum types as typed disjoint unions. Subclasses in OO languages on the other hand share data layout, whereas in Haskell, the data constructors can be completely disjoint. – Tobias
Bool is a type, while False and True are values. A key difference lies in that. – duplode
To see it, try :k at GHCi propmt:
~> :k Bool
Bool :: *
~> :k False
***error***
This is because Bool is a type of things, and False (creates) a thing. It is a (nullary) data constructor, which happens to not require any arguments:
x :: Bool
x = False
Things have types:
~> :t False
False :: Bool
~> :t x
x :: Bool
~> :t Bool
***error***

Related

The limit set of types with new data like `Tree a`

Exploring and studing type system in Haskell I've found some problems.
1) Let's consider polymorphic type as Binary Tree:
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Show
And, for example, I want to limit my considerations only with Tree Int, Tree Bool and Tree Char. Of course, I can make a such new type:
data TreeIWant = T1 (Tree Int) | T2 (Tree Bool) | T3 (Tree Char) deriving Show
But could it possible to make new restricted type (for homogeneous trees) in more elegant (and without new tags like T1,T2,T3) way (perhaps with some advanced type extensions)?
2) Second question is about trees with heterogeneous values. I can do them with usual Haskell, i.e. I can do the new helping type, contained tagged heterogeneous values:
data HeteroValues = H1 Int | H2 Bool | H3 Char deriving Show
and then make tree with values of this type:
type TreeH = Tree HeteroValues
But could it possible to make new type (for heterogeneous trees) in more elegant (and without new tags like H1,H2,H3) way (perhaps with some advanced type extensions)?
I know about heterogeneous list, perhaps it is the same question?
For question #2, it's easy to construct a "restricted" heterogeneous type without explicit tags using a GADT and a type class:
{-# LANGUAGE GADTs #-}
data Thing where
T :: THING a => a -> Thing
class THING a
Now, declare THING instances for the the things you want to allow:
instance THING Int
instance THING Bool
instance THING Char
and you can create Things and lists (or trees) of Things:
> t1 = T 'a' -- Char is okay
> t2 = T "hello" -- but String is not
... type error ...
> tl = [T (42 :: Int), T True, T 'x']
> tt = Branch (Leaf (T 'x')) (Leaf (T False))
>
In terms of the type names in your question, you have:
type HeteroValues = Thing
type TreeH = Tree Thing
You can use the same type class with a new GADT for question #1:
data ThingTree where
TT :: THING a => Tree a -> ThingTree
and you have:
type TreeIWant = ThingTree
and you can do:
> tt1 = TT $ Branch (Leaf 'x') (Leaf 'y')
> tt2 = TT $ Branch (Leaf 'x') (Leaf False)
... type error ...
>
That's all well and good, until you try to use any of the values you've constructed. For example, if you wanted to write a function to extract a Bool from a possibly boolish Thing:
maybeBool :: Thing -> Maybe Bool
maybeBool (T x) = ...
you'd find yourself stuck here. Without a "tag" of some kind, there's no way of determining if x is a Bool, Int, or Char.
Actually, though, you do have an implicit tag available, namely the THING type class dictionary for x. So, you can write:
maybeBool :: Thing -> Maybe Bool
maybeBool (T x) = maybeBool' x
and then implement maybeBool' in your type class:
class THING a where
maybeBool' :: a -> Maybe Bool
instance THING Int where
maybeBool' _ = Nothing
instance THING Bool where
maybeBool' = Just
instance THING Char where
maybeBool' _ = Nothing
and you're golden!
Of course, if you'd used explicit tags:
data Thing = T_Int Int | T_Bool Bool | T_Char Char
then you could skip the type class and write:
maybeBool :: Thing -> Maybe Bool
maybeBool (T_Bool x) = Just x
maybeBool _ = Nothing
In the end, it turns out that the best Haskell representation of an algebraic sum of three types is just an algebraic sum of three types:
data Thing = T_Int Int | T_Bool Bool | T_Char Char
Trying to avoid the need for explicit tags will probably lead to a lot of inelegant boilerplate elsewhere.
Update: As #DanielWagner pointed out in a comment, you can use Data.Typeable in place of this boilerplate (effectively, have GHC generate a lot of boilerplate for you), so you can write:
import Data.Typeable
data Thing where
T :: THING a => a -> Thing
class Typeable a => THING a
instance THING Int
instance THING Bool
instance THING Char
maybeBool :: Thing -> Maybe Bool
maybeBool = cast
This perhaps seems "elegant" at first, but if you try this approach in real code, I think you'll regret losing the ability to pattern match on Thing constructors at usage sites (and so having to substitute chains of casts and/or comparisons of TypeReps).

Understanding Haskell's Bool Deriving an Ord

Learn You a Haskell presents the Bool type:
data Bool = False | True deriving (Ord)
I don't understand the reason for comparing Bool's.
> False `compare` True
LT
> True `compare` False
GT
What would be lost if Bool did not derive from Ord?
Bool forms a bounded lattice* where False is bottom and True is top. This bounded lattice defines a (total) ordering where False really is strictly less than True. (They are also the only elements of this lattice.)
The boolean operations and and or can also be looked at as meet and join, respectively, in this lattice. Meet finds the greatest lower bound and join finds the least upper bound. This means that a && False = False is the same thing as saying that the lower bound of bottom and anything else is bottom, and a || True = True is the same thing as saying that the upper bound of top and anything is top. So meet and join, which use the ordering property of the booleans, are equivalent to the boolean operations you are familiar with.
You can use min and max to show this in Haskell:
False `min` True = False -- this is the greatest lower bound
False && True = False -- so is this
False `max` True = True -- this is the least upper bound
False || True = True -- so is this
This shows that you can define && and || just from the derived Ord instance:
(&&) = min
(||) = max
Note that these definitions are not equivalent in the presence of a different kind of bottom because (&&) and (||) are short-circuiting (non-strict in the second argument when the first is False or True, respectively) while min and max are not.
Also, a small correction: The deriving clause does not say thatBool "derives from" Ord. It instructs GHC to derive an instance of the typeclass Ord for the type Bool.
* More specifically, a complemented distributive lattice. More specifically still, a boolean algebra.
The Ord instance for Bool becomes much more important when you need to compare values that contain Bool somewhere inside. For example, without it we wouldn't be able to write expressions like:
[False,True] `compare` [False,True,False]
(3, False) < (3, True)
data Person = Person { name :: String, member :: Bool } deriving (Eq, Ord)
etc.
It is because Haskell designers made a mistake! I never saw a mathematics textbook that mentioned ordering of booleans. Just beacuse they can be it does not mean with should. Some of us use Haskell exactly because it disallows/protects us from confusing/nonsensical things in many cases but not this one.
instance Ord Bool causes a => b to mean what you expect a <= b to mean!
Earlier arguments in favour of instance Ord Bool where that you can make more types comparable implicitly. Continuing that line of argument some might want to make every type comparable impicitly and even have weak dynamic typing and omit type classes altogether. But we want strong typing exactly to disallow what is not obviously correct, and instance Ord Bool defeats that purpose.
As for the argument that Bool is a bounded lattice. Unlike boolean:={True,False}, what we have in Haskell is Bool:={True,False,bottom} is no longer a bounded lattice since neither True nor False are identity elements in the presense of bottom. That is related to those comments discussing && vs min etc.

Why does Haskell not have records with structural typing?

I have heard Haskell described as having structural typing. Records are an exception to that though as I understand. For example foo cannot be called with something of type HRec2 even though HRec and HRec2 are only nominally different in their fields.
data HRec = HRec { x :: Int, y :: Bool }
data HRec2 = HRec2 { p :: Int, q :: Bool }
foo :: HRec -> Bool
Is there some explanation for rejecting extending structural typing to everything including records?
Are there statically typed languages with structural typing even for records? Is there maybe some debate on this I can read about for all statically typed languages in general?
Haskell has structured types, but not structural typing, and that's not likely to change.*
The refusal to permit nominally different but structurally similar types as interchangeable arguments is called type safety. It's a good thing. Haskell even has a newtype declaration to provide types which are only nominally different, to allow you to enforce more type safety. Type safety is an easy way to catch bugs early rather than permit them at runtime.
In addition to amindfv's good answer which includes ad hoc polymorphism via typeclasses (effectively a programmer-declared feature equivalence), there's parametric polymorphism where you allow absolutely any type, so [a] allows any type in your list and BTree a allows any type in your binary tree.
This gives three answers to "are these types interchangeable?".
No; the programmer didn't say so.
Equivalent for a specific purpose because the programmer said so.
Don't care - I can do the same thing to this collection of data because it doesn't use any property of the data itself.
There's no 4: compiler overrules programmer because they happened to use a couple of Ints and a String like in that other function.
*I said Haskell is unlikely to change to structural typing. There is some discussion to introduce some form of extensible records, but no plans to make (Int,(Int,Int)) count as the same as (Int, Int, Int) or Triple {one::Int, two::Int, three::Int} the same as Triple2 {one2::Int, two2::Int, three2::Int}.
Haskell records aren't really "less structural" than the rest of the type system. Every type is either completely specified, or "specifically vague" (i.e. defined with a typeclass).
To allow both HRec and HRec2 to f, you have a couple of options:
Algebraic types:
Here, you define HRec and HRec2 records to both be part of the HRec type:
data HRec = HRec { x :: Int, y :: Bool }
| HRec2 { p :: Int, q :: Bool }
foo :: HRec -> Bool
(alternately, and maybe more idiomatic:)
data HRecType = Type1 | Type2
data HRec = HRec { hRecType :: HRecType, x :: Int, y :: Bool }
Typeclasses
Here, you define foo as able to accept any type as input, as long as a typeclass instance has been written for that type:
data HRec = HRec { x :: Int, y :: Bool }
data HRec2 = HRec2 { p :: Int, q :: Bool }
class Flexible a where
foo :: a -> Bool
instance Flexible HRec where
foo (HRec a _) = a == 5 -- or whatever
instance Flexible HRec2 where
foo (HRec2 a _) = a == 5
Using typeclasses allows you to go further than regular structural typing -- you can accept types that have the necessary information embedded in them, even if the types don't superficially look similar, e.g.:
data Foo = Foo { a :: String, b :: Float }
data Bar = Bar { c :: String, d :: Integer }
class Thing a where
doAThing :: a -> Bool
instance Thing Foo where
doAThing (Foo x y) = (x == "hi") && (y == 0)
instance Thing Bar where
doAThing (Bar x y) = (x == "hi") && ((fromInteger y) == 0)
We can run fromInteger (or any arbitrary function) to get the data we need from what we have!
I'm aware of two library implementations of structurally typed records in Haskell:
HList is older, and is explained in an excellent paper: Haskell's overlooked object system (free online, but SO won't let me include more links)
vinyl is newer, and uses fancy new GHC features. There's at least one library, vinyl-gl, using it.
I cannot answer the language-design part of your question, though.
To answer your last question, Go and Scalas definitely have structural typing. Some people (including me) would call that "statically unsafe typing", since it implicitly declares all samely- named methods in a program to have the same semantics, which implies "spooky action at a distance", relating code in on source file to code in some library that the program has never seen.
IMO, better to require that the same-named methods to explicitly declare that they conform to a named semantic "model" of behavior.
Yes, the compiler would guarantee that the method is callable, but it isn't much safer than saying:
f :: [a] -> Int
And letting the compiler choose an arbitrary implementation that may or may not be length.
(A similar idea can be made safe with Scala "implicits" or Haskell (GHC?) "reflection" package.)

Can I differentiate between typeclass instances at runtime?

Is it possible to implement a type class as follows:
class SomeClass e where
isEq :: (SomeClass e') => e -> e' -> Bool
where isEq x y would return true when x and y are both the same instance of this type class?
Context: This is a thinly-veiled attempt at getting run-time type tests. While at first I read that Haskell has type erasure, I've also read that with recent extensions to GHC, some run-time information in there.
edit: for those wondering about my usecase... I've been using type-level programming to ensure certain properties of some of my ADTs, namely involving resource usage. These resources are represented by distinct types (and resource locks are accordingly implemented at type level).
I'm attempting to write an optimisation procedure that requires identification of resource writing/reading. But since all my resources are represented by distinct singletons, whose only common factor (apart from all values being bottom) is the typeclass which groupes them together.
In a nutshell I want to use the results of my type-level programming at a run-time, value level.
You can do this, as hinted at by Karolis Juodelė, with Data.Typeable:
Prelude Data.Typeable> :{
Prelude Data.Typeable| let isEq :: (Typeable a , Typeable b) => a -> b -> Bool
Prelude Data.Typeable| isEq x y = typeOf x == typeOf y
Prelude Data.Typeable| :}
Prelude Data.Typeable> isEq True ()
False
Prelude Data.Typeable> isEq True False
True
The question is: Why would you not know at run time what the types are, and then why would you care about whether they are equal – can you elaborate your use case?

Determine whether a value is a function in Haskell

Is it possible to write a function isFunc :: a -> Bool to determine whether an arbitrary value is a function (of any kind) such that
foo :: Int -> Int
bar :: Char -> Char -> Char
> isFunc foo
True
> isFunc bar
True
> isFunc 3
False
> isFunc 'a'
False
I'm using Data.Dynamic so I can't determine the type in advance.
What are you asking for and what you need to do with Data.Dynamic seem to be different things. You need to know the exact type of value before extracting it with fromDyn/fromDynamic. To determine whether Dynamic contains a function value you need to analyze TypeRep:
isFuncDynamic x = typeRepTyCon (dynTypeRep x) == typeRepTyCon (typeOf2 id)
(Forgive me if this is not the most concise implementation.)
Parametricity says no. The only functions of type
a -> Bool
are constant functions.
However, with a bit of ad hoc polymorphism and a bit more chutzpah, you can do this:
{-# LANGUAGE OverlappingInstances, FlexibleInstances #-}
class Sick x where
isFunc :: x -> Bool
instance Sick (a -> b) where
isFunc _ = True
instance Sick x where
isFunc _ = False
and then it looks like you have
*Sick> isFunc 3
False
*Sick> isFunc id
True
But it does seem like a peculiar thing to do. What use is the resulting Bool to you?

Resources