mirror of
https://github.com/thma/WhyHaskellMatters.git
synced 2024-11-25 18:56:48 +03:00
explain Ord, write conclusion
This commit is contained in:
parent
5fa15ff8b7
commit
8a257be84c
73
README.md
73
README.md
@ -680,7 +680,8 @@ fac' n = prod [1..n]
|
|||||||
|
|
||||||
This is going to be a very short section. In Haskell all data is immutable. Period.
|
This is going to be a very short section. In Haskell all data is immutable. Period.
|
||||||
|
|
||||||
Let's look at some interactions with the Haskell GHCi REPL:
|
Let's look at some interactions with the Haskell GHCi REPL (whenever you see the `λ>` prompt in this article
|
||||||
|
it is from a GHCi session):
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
λ> a = [1,2,3]
|
λ> a = [1,2,3]
|
||||||
@ -1186,6 +1187,26 @@ data PairStatusSeverity = PSS Status Severity deriving (Eq)
|
|||||||
|
|
||||||
This automatic deriving of type class instances works for many cases and reduces a lof of repetitive code.
|
This automatic deriving of type class instances works for many cases and reduces a lof of repetitive code.
|
||||||
|
|
||||||
|
For example, its possible to automatically derive instances of the `Ord` type class, which provides
|
||||||
|
ordering functionality:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
class (Eq a) => Ord a where
|
||||||
|
compare :: a -> a -> Ordering
|
||||||
|
(<), (<=), (>), (>=) :: a -> a -> Bool
|
||||||
|
max, min :: a -> a -> a
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using `deriving` for the `Status` and `Severity` types, the Compiler will implement the
|
||||||
|
ordering according to the ordering of the constructors in the type declaration.
|
||||||
|
That is `Green < Yellow < Red` and `Low < Middle < High`:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
data Status = Green | Yellow | Red deriving (Eq, Ord)
|
||||||
|
data Severity = Low | Middle | High deriving (Eq, Ord)
|
||||||
|
```
|
||||||
|
|
||||||
### Read and Show
|
### Read and Show
|
||||||
|
|
||||||
Two other quite useful type classes are `Read` and `Show` that also support automatic deriving.
|
Two other quite useful type classes are `Read` and `Show` that also support automatic deriving.
|
||||||
@ -1373,6 +1394,9 @@ maxStatus' = maximum statusTree
|
|||||||
treeSize = length statusTree
|
treeSize = length statusTree
|
||||||
|
|
||||||
-- in GHCi:
|
-- in GHCi:
|
||||||
|
λ> :t max
|
||||||
|
max :: Ord a => a -> a -> a
|
||||||
|
|
||||||
λ> foldr max Green statusTree
|
λ> foldr max Green statusTree
|
||||||
Red
|
Red
|
||||||
-- using maximum from Foldable type class:
|
-- using maximum from Foldable type class:
|
||||||
@ -1468,7 +1492,7 @@ In the `Just` case we proceed to the next processing step.
|
|||||||
|
|
||||||
This kind of handling is repetitive and buries the actual intention under a lot of boilerplate.
|
This kind of handling is repetitive and buries the actual intention under a lot of boilerplate.
|
||||||
As Haskell uses layout (i.e. indentation) instead of curly brackets to separate blocks the code will
|
As Haskell uses layout (i.e. indentation) instead of curly brackets to separate blocks the code will
|
||||||
end up in what is called the *dreaded staircase*: it maches to the right of the screen.
|
end up in what is called the *dreaded staircase*: it marches to the right of the screen.
|
||||||
|
|
||||||
So we are looking for a way to improve the code by abstracting away the chaining of functions that return
|
So we are looking for a way to improve the code by abstracting away the chaining of functions that return
|
||||||
`Maybe` values and providing a way to *short circuit* the `Nothing` cases.
|
`Maybe` values and providing a way to *short circuit* the `Nothing` cases.
|
||||||
@ -1565,6 +1589,11 @@ In the next section we will learn how exactly this works.
|
|||||||
|
|
||||||
### Explicit side effects with the IO Monad
|
### Explicit side effects with the IO Monad
|
||||||
|
|
||||||
|
> Monadic I/O is a clever trick for encapsulating sequential, imperative computation, so that it can “do no evil”
|
||||||
|
> to the part that really does have precise semantics and good compositional properties.
|
||||||
|
>
|
||||||
|
> [Conal Elliott](http://conal.net/blog/posts/is-haskell-a-purely-functional-language)
|
||||||
|
|
||||||
The most prominent Haskell Monad is the `IO` monad. It is used to compose operations that perform I/O.
|
The most prominent Haskell Monad is the `IO` monad. It is used to compose operations that perform I/O.
|
||||||
We'll study this with a simple example.
|
We'll study this with a simple example.
|
||||||
|
|
||||||
@ -1582,7 +1611,7 @@ catch away the `IOException`).
|
|||||||
|
|
||||||
So how can we use the result of `getLine` in a function that takes a `String` value as input parameter?
|
So how can we use the result of `getLine` in a function that takes a `String` value as input parameter?
|
||||||
|
|
||||||
We need the monadic bind operation `(>>=)` to do this in the same as we saw in the `Maybe` monad:
|
We need the monadic bind operation `(>>=)` to do this in the same as we already saw in the `Maybe` monad:
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
-- convert a string to upper case
|
-- convert a string to upper case
|
||||||
@ -1594,6 +1623,7 @@ up =
|
|||||||
getLine >>= \str ->
|
getLine >>= \str ->
|
||||||
print (strToUpper str)
|
print (strToUpper str)
|
||||||
|
|
||||||
|
-- and then in GHCi:
|
||||||
λ> :t print
|
λ> :t print
|
||||||
print :: Show a => a -> IO ()
|
print :: Show a => a -> IO ()
|
||||||
λ> up
|
λ> up
|
||||||
@ -1610,19 +1640,48 @@ up' = do
|
|||||||
```
|
```
|
||||||
|
|
||||||
Making side effects explicit in function type signatures is one of the most outstanding achievements of Haskell.
|
Making side effects explicit in function type signatures is one of the most outstanding achievements of Haskell.
|
||||||
This feature will lead to a very clear distinction between code that is free of side effects (aka *pure* code) and code
|
This feature will lead to a very rigid distinction between code that is free of side effects (aka *pure* code) and code
|
||||||
that has side effects (aka *impure* code).
|
that has side effects (aka *impure* code).
|
||||||
|
|
||||||
Keeping domain logic *pure* - particularly when working only with *total* functions - will dramatically improve
|
Keeping domain logic *pure* - particularly when working only with *total* functions - will dramatically improve
|
||||||
reliability and testability as tests can be run without setting up mocks or real backends.
|
reliability and testability as tests can be run without setting up mocks or stubbed backends.
|
||||||
|
|
||||||
It's not possible to introduce side effects without making them explicit in type signatures.
|
It's not possible to introduce side effects without making them explicit in type signatures.
|
||||||
There is nothing like the *invisible* Java `RuntimeExceptions`.
|
There is nothing like the *invisible* Java `RuntimeExceptions`.
|
||||||
So you can rely on the compiler to detect any violations of a rule like "No impure code in domain logic".
|
So you can rely on the compiler to detect any violations of a rule like "No impure code in domain logic".
|
||||||
|
|
||||||
I've written a very simple Restaurant Booking Service that explains how Haskell helps you to keep domain logic pure by
|
I've written a simple Restaurant Booking REST Service API that explains how Haskell helps you to keep domain logic pure by
|
||||||
organizing your code according to the [ports and adapters pattern](https://github.com/thma/RestaurantReservation).
|
organizing your code according to the [ports and adapters pattern](https://github.com/thma/RestaurantReservation).
|
||||||
|
|
||||||
|
The section on type classes (and on Monads in particular) have been quite lengthy. Yet, they have hardly shown more than
|
||||||
|
the tip of the iceberg. If you want to dive deeper into type classes, I recommend
|
||||||
|
[The Typeclassopedia](https://wiki.haskell.org/Typeclassopedia).
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
tbd.
|
We have covered quite a bit of terrain in the course of this article.
|
||||||
|
|
||||||
|
It may seem that Haskell has invented an intimidating mass of non-mainstream concepts.
|
||||||
|
But in fact, Haskell inherits much from earlier functional programming languages.
|
||||||
|
|
||||||
|
Features like first class functions, comprehensive list api or declarative programming
|
||||||
|
had already been introduced with Lisp and Scheme.
|
||||||
|
|
||||||
|
Several others like pattern matching, non-strict evaluation, immutability, purity, static and strong typing,
|
||||||
|
type inference, algebraic data types and polymorphic data types
|
||||||
|
have been invented in languages like Hope, Miranda an ML.
|
||||||
|
|
||||||
|
Only a few features like type classes and explicit side effects / monadic I/O have first been introduced in Haskell.
|
||||||
|
|
||||||
|
So if you already know some functional languages, Haskell will not be to alien to you.
|
||||||
|
For developers with a background in OO languages, the conceptual gap will be much larger.
|
||||||
|
|
||||||
|
I hope that this article helped to bridge that gap a bit and to better explain [why
|
||||||
|
functional programming](https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf) - and Haskell in particular - matters.
|
||||||
|
|
||||||
|
Using functional programming languages - or applying some of the techniques shown above - will help
|
||||||
|
to create designs that are closer to the problem domain (as intented by domain driven design),
|
||||||
|
more readable (due to their declarative character), allow equational reasoning,
|
||||||
|
are more flexible for future changes or extensions, provide better testability (supporting BDD, TDD and property based testing),
|
||||||
|
will need much less debugging, are better to maintain and, last not least, will be more fun to write.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user