diff --git a/exercises/Examples/QuickCheck.hs b/exercises/Examples/QuickCheck.hs index eafb992..4e82bf8 100644 --- a/exercises/Examples/QuickCheck.hs +++ b/exercises/Examples/QuickCheck.hs @@ -12,7 +12,7 @@ propRevSmall :: Property propRevSmall = rev [1,2] === [2,1] propRevTwice :: [Int] -> Property -propRevTwice xs = reverse (reverse xs) === xs +propRevTwice xs = rev (rev xs) === xs propRevMedium :: Property propRevMedium = conjoin [rev [1,2,2] === [2,2,1], diff --git a/exercises/Set16a.hs b/exercises/Set16a.hs index f89dfd4..cf8461b 100644 --- a/exercises/Set16a.hs +++ b/exercises/Set16a.hs @@ -91,7 +91,7 @@ freq2 xs = map (\x -> (x,1)) xs -- -- You can assume that the output is nonempty. -- --- Examples: TODO fix +-- Examples: -- *Set16a> quickCheck (outputInInput "abb" [('a',1)]) -- +++ OK, passed 100 tests. -- *Set16a> quickCheck (outputInInput "abb" [('a',1),('x',3)]) diff --git a/part2.html b/part2.html index f19b48a..67c605a 100644 --- a/part2.html +++ b/part2.html @@ -347,7 +347,7 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni Either String Int -Nil, Cons True Nil, Const True (Cons False Nil) … +Nil, Cons True Nil, Cons True (Cons False Nil) … List Bool @@ -4036,7 +4036,7 @@ liftA2 sumMoney (parseMoney "123" "e") (parseMoney "xxx parseAnswer "n" ==> Just No parseAnswer "maybe" ==> Just Yes parseAnswer "x" ==> Nothing -

How could we generalize eitherOf? We can’t give it the type Applicative f => f x -> f x -> f x because then the implementation would need to be effectively something like eitherOf a b = liftA2 something a b, but then eitherOf Nothing (Just x) would be Nothing (since that’s how the instance Applicative instance works)!

+

How could we generalize eitherOf? We can’t give it the type Applicative f => f x -> f x -> f x because then the implementation would need to be effectively something like eitherOf a b = liftA2 something a b, but then eitherOf Nothing (Just x) would be Nothing (since that’s how the Applicative instance works)!

It turns out we need a new type class: Alternative. An Alternative adds two operations to Applicative: empty means no results, and <|> means combining results.

class Applicative f => Alternative f where
   empty :: f a
@@ -4247,7 +4247,7 @@ TODO explain `some` and `many`
 
-

We can write and individual test case using the === operator from QuickCheck:

+

We can write an individual test case using the === operator from QuickCheck:

@@ -4256,7 +4256,7 @@ TODO explain `some` and `many` +++ OK, passed 1 test.

So far so good. However this isn’t really what QuickCheck was made for. QuickCheck was designed for property-based testing where you can state a property your code should have, and QuickCheck runs your code with randomized inputs, checking the property every time. What would be a simple property that a correct implementation of reverse has? Reversing a list twice should certainly give us back the same list. Let’s write it out:

propRevTwice :: [Int] -> Property
-propRevTwice xs = reverse (reverse xs) === xs
+propRevTwice xs = rev (rev xs) === xs

Our Property has an argument, which means that QuickCheck will generate random values and run the test. We can use the verboseCheck function to see which values are run. We can also give a parameter to the test ourselves if we wish to check a specific value.

*Examples.QuickCheck> quickCheck propRevTwice
 +++ OK, passed 100 tests.
@@ -4522,7 +4522,7 @@ TODO explain `some` and `many`
 Money 0.819672131147541
 *Examples.Phantom> convertUnsafe eurToUsd dollar
 Money 1.22
-

Note! The words currency, from, to and so for in the previous examples are just type variables. There’s nothing special going on with them. We could’ve as well given invert a type like Rate a b -> Rate b a without any change in type safety.

+

Note! The words currency, from, to and so forth in the previous examples are just type variables. There’s nothing special going on with them. We could’ve as well given invert a type like Rate a b -> Rate b a without any change in type safety.

This approach that uses phantom types has clear benefits: giving us type errors for invalid code. Also, compared to defining lots of concrete types like data MoneyEur = MoneyEur Double, with phantom types we need to implement functions like scaleMoney and addMoney only once. Also, we’re able to define polymorphic and reusable concepts like Rate. You can contrast this approach with the Boxing section of Lecture 7.

However, phantom types also have downsides. Without advanced tricks we can’t really handle currencies that are defined at runtime (for example: reading an amount from a user). It’s also easy to end up in a place where you start needing language extensions like Generalized Algebraic Datatypes, type families and other type-level programming constructs. Eventually you’re all the way in the world of dependent typing.

So what are good applications for phantom types? When you need to track some simple, but crucial information, that is known at compile-time. An example that’s somewhat better than currencies is tracking whether inputs from the user have been sanitated to prevent attacks like SQL injection or cross-site scripting.

@@ -4566,7 +4566,7 @@ TODO explain `some` and `many`

That’s almost 2 times as fast, on the 2-core machine that this example is being run on. Pretty nice. The coolest thing here is that we were able to define the computation (map fib ...) completely separately from the evaluation strategy (parList rseq), separating the what to compute from the how to compute.

16.3.2 Concurrency

Computer science makes the distinction between parallel and concurrent computations. Parallel computations are those that just run separate independent computations in parallel (in other words, parallelism is pure). Concurrent computations are those where there are multiple interacting threads of computation. Concurrency usually involves threads, locks, messages and deadlocks.

-

In addition to great tooling for parallelism, Haskell also has good tooling for concurrency via threads. Since concurrency is all about side-effects, concurrent computations happen in the IO Monad. The classic example of threading is two threads, the other one printing a stream of As and the other a stream of Bs. Here it is in Haskell:

+

In addition to great tooling for parallelism, Haskell also has good tooling for concurrency via threads. Since concurrency is all about side-effects, concurrent computations happen in the IO Monad. The classic example of threading is two threads, one printing a stream of As and the other a stream of Bs. Here it is in Haskell:

printA :: IO ()
 printA = putStrLn (replicate 40 'A')
 
@@ -4582,7 +4582,7 @@ TODO explain `some` and `many`
 AABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABB

The operation forkIO :: IO () -> IO ThreadId takes an IO operation and starts running it in the backgroud. It produces a ThreadId that can be used to e.g. terminate the thread.

If we want to add actual communication between threads, we can use abstractions like MVar (a mutable thread-safe variable) or Chan (a queue).

-

Here’s a simple example where one thread writes a values to an MVar and another one waits for them and prints them. An MVar works like a mailbox: it is either empty or full. Calling takeMVar on an empty box waits for the box to get filled (with a putMVar). Symmetrically, trying to putMVar into a full box waits until the box is empty.

+

Here’s a simple example where one thread writes a value to an MVar and another one waits for them and prints them. An MVar works like a mailbox: it is either empty or full. Calling takeMVar on an empty box waits for the box to get filled (with a putMVar). Symmetrically, trying to putMVar into a full box waits until the box is empty.

takeMVar :: MVar a -> IO a
 putMVar :: MVar a -> a -> IO ()
 newEmptyMVar :: IO (MVar a)
@@ -4611,7 +4611,7 @@ AABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABB

16.5 Where to Go From Here?

Congratulations! You’ve reached the end of this two part course on Functional Programming in Haskell. What next? You definitely know enough Haskell to keep learning on your own. The online Haskell community is very friendly and there are lots of blog posts and other content explaining advanced techniques and features. You can find lots of interesting stuff by following for example: