From 71a397d60190fbf83653e6023eb78c77ad9f5be2 Mon Sep 17 00:00:00 2001 From: "Mahler, Thomas" Date: Mon, 13 Apr 2020 21:25:57 +0200 Subject: [PATCH] start with IO --- README.md | 125 ++++++++++++++++++++++++++++++++++++--------- src/TypeClasses.hs | 14 +++++ 2 files changed, 114 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 7a6cf8f..7420842 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ [![Actions Status](https://github.com/thma/WhyHaskellMatters/workflows/Haskell%20CI/badge.svg)](https://github.com/thma/WhyHaskellMatters/actions) -This is an early draft of a work in progress article. - > Haskell doesn't solve different problems than other languages. > But it solves them differently. > @@ -37,20 +35,26 @@ nevertheless this article is by no means a complete introduction to the language - [Declarative programming](#declarative-programming) - [Non-strict Evaluation](#non-strict-evaluation) - [Type Classes](#type-classes) - -[Functor and Foldable](#functor-and-foldable) + - [Functor and Foldable](#functor-and-foldable) + - [Functor](#functor) + - [Foldable](#foldable) + - [The Maybe Monad](#the-maybe-monad) + - [Total Functions](#total-functions) + - [Composition of Maybe operations](#composition-of-maybe-operations) + - [Explicit side effects with the IO Monad](#explicit-side-effects-with-the-io-monad) ## Introduction -Exactly thirty years ago, on April 1st 1990, the original Haskell language report was published -by a small group of academic researchers in the field of non-strict functional programming. +Exactly thirty years ago, on April 1st 1990, a small group of researchers in the field of non-strict functional +programming published the original Haskell language report. -Haskell never became one of the most popular languages in the software industry or part of the mainstream +Haskell never became one of the most popular languages in the software industry or part of the mainstream, but it has been and still is quite influential in the software development community. -In this article I want to explain why Haskell keeps to be such an important language by presenting some +In this article I try to explain why Haskell keeps being such an important language by presenting some of its most distinguishing features and detailing them with working code examples. -But before diving directly into the technical details I'd like to first have a closer look on the reception of +Before diving directly into the technical details I'd like to first have a closer look on the reception of Haskell in the software developers community: ### A strange development over time @@ -60,21 +64,21 @@ since its beginnings in the 1980ies Simon Peyton Jones speaks about the rather unusual life story of Haskell. First he talks about the typical life cycle of research languages. They are often created by -a single researcher (who is also the single user) and most of them will be abandoned +a single researcher (who is also the single user), and most of them will be abandoned after just a few years. A more successful research language might gain some interest in a larger community but will still not escape the ivory tower and typically will be given up within ten years. On the other hand we have all those popular programming languages that are quickly adopted by -large numbers of users and thus reach "the threshold of immortality". +large numbers of developers and thus reach "the threshold of immortality". That is the base of existing code will grow so large that the language will be in use for decades. A little jokingly he then depicts the sad fate of languages designed by committees by flat line through zero: They simply never take off. -Finally he presents a chart showing the Haskell timeline: +Finally, he presents a chart showing the Haskell timeline: ![the haskell timeline](img/language-5.png) @@ -82,10 +86,10 @@ The development shown in this chart seems rather unexpected: Haskell started as a research language and was even designed by a committee; so in all probability it should have been abandoned long before the millennium! -But instead it gained some momentum in its early years followed by a rather quiet phase during -the decade of OO hype (Java was released in 1995). +Instead, it gained some momentum in its early years followed by a rather quiet phase during +the decade of OO hype (Java being released in 1995). And then again we see a continuous growth of interest since about 2005. -I'm writing this in early 2020 and we still see this trend! +I'm writing this in early 2020, and we still see this trend! ### Being used versus being discussed @@ -95,16 +99,16 @@ In statics that rank programming languages by actual usage Haskell is typically But in statistics that instead rank languages by the volume of discussions on the internet Haskell typically scores much better (often in the top ten). -### So why does Haskell keep to be a hot topic in the software development community? +### So why does Haskell keep being a hot topic in the software development community? A very *short answer* might be: Haskell has a number of features that are clearly different from those of most other programming languages. Many of these features have proven to be powerful tools to solve basic problems of software development elegantly. -Therefore over time other programming languages have adopted parts of these concepts (e.g. pattern matching or type classes). +Therefore, over time other programming languages have adopted parts of these concepts (e.g. pattern matching or type classes). In discussions about such concepts the Haskell heritage is mentioned and differences between the original Haskell concepts and those of other languages are discussed. -Sometimes people are encouraged to have a closer look at the source of these concepts to get a deeper understanding of +Sometimes people feel encouraged to have a closer look at the source of these concepts to get a deeper understanding of their original intentions. That's why we see a growing number of developers working in Python, Typescript, Scala, Rust, C++, C# or Java starting to dive into Haskell. @@ -128,7 +132,7 @@ studying some of the most distinguishing features of Haskell. We'll go through this one by one: -### functions can be assigned to variables exactly as any other other values +### functions can be assigned to variables exactly as any other values Let's have a look how this looks like in Haskell. First we define some simple values: @@ -1513,9 +1517,84 @@ This looks quite like a sequence of statements (including variable assignments) Due to this similarity Monads have been aptly called [programmable semicolons](http://book.realworldhaskell.org/read/monads.html#id642960) But as we have seen: below the syntactic sugar it's a purely functional composition! -### The IO Monad +### Purity + +A function is called pure if it corresponds to a function in the mathematical sense: it associates each possible input +value with an output value, and does nothing else. In particular, + +- it has no side effects, that is to say, invoking it produces no observable effect other than the result it returns; + it cannot also e.g. write to disk, or print to a screen. +- it does not depend on anything other than its parameters, so when invoked in a different context or at a different + time with the same arguments, it will produce the same result. + +Purity makes it easy to reason about code, as it is so close to the mathematical calculus. +The properties of Haskell programs can thus often be determined with equational reasoning. + +Purity also improves testability: It is much easier to set up tests without worrying about mocks or stubs to factor out +access to backend layers. + + + +### Explicit side effects with the IO Monad 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. + +In an imperative language, reading a String from the console simply returns a String value (e.g. `BufferedReader.readline()` in Java: +`public String readLine() throws IOException`). + +In Haskell the function `getLine` does not return a `String` value but an `IO String`: + +```haskell +getLine :: IO String +``` +This could be interpreted as: `getLine` returns a String in an IO context. +In Haskell, it is not possible to extract the String value from its IO context (In Java on the other hand you could always +catch away the `IOException`). + +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 achieve this: + +```haskell +-- convert a string to upper case +strToUpper :: String -> String +strToUpper = map toUpper + +up :: IO () +up = + getLine >>= \str -> + print (strToUpper str) + +λ> :t print +print :: Show a => a -> IO () +λ> up +hello world +"HELLO WORLD" +``` + +or with do-Notation: +```haskell +up' :: IO () +up' = do + str <- getLine + print (strToUpper str) +``` + +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 +that has side effects (aka *impure* code). + +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. + +It's not possible to introduce side effects without making them explicit in type signatures. +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". + +I've written a very simple Restaurant Booking Service 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). + --- @@ -1546,9 +1625,7 @@ Damit lässt sich Seiteneffektfreie Programmierung realisieren ("Purity") - Hohe Abstraktion, Programme lassen sich oft wie eine deklarative Spezifikation des Algorithmus lesen - sehr gute Testbarkeit durch "Composability" - - - das "ports & adapters" Beispiel: https://github.com/thma/RestaurantReservation - + - TDD / DDD - Memory Management (sehr schneller GC) @@ -1563,9 +1640,7 @@ Damit lässt sich Seiteneffektfreie Programmierung realisieren ("Purity") ## toc for code chapters (still in german) - TypKlassen - - - explizite Seiten Effekte -> IO Monade - + - static typing - Testbarkeit diff --git a/src/TypeClasses.hs b/src/TypeClasses.hs index 354f198..7f951c8 100644 --- a/src/TypeClasses.hs +++ b/src/TypeClasses.hs @@ -2,6 +2,7 @@ module TypeClasses where import AlgebraicDataTypes (Status (..), Severity (..), PairStatusSeverity (..), Tree (..)) import Data.Foldable +import Data.Char (toUpper) instance Num Char where a + b = toEnum (fromEnum a + fromEnum b) @@ -56,6 +57,19 @@ maxStatus' = maximum statusTree treeSize = length statusTree +strToUpper :: String -> String +strToUpper = map toUpper + +up :: IO () +up = + getLine >>= \str -> + print (strToUpper str) + +up' :: IO () +up' = do + str <- getLine + print (strToUpper str) + greet = do putStr "Please enter your first name" first <- getLine