mirror of
https://github.com/thma/LtuPatternFactory.git
synced 2024-12-04 12:43:14 +03:00
work on infinite data structures and laziness
This commit is contained in:
parent
2bd5a5b893
commit
38cead5ca2
118
README.md
118
README.md
@ -1375,14 +1375,6 @@ TBD
|
||||
|
||||
## Beyond type class patterns
|
||||
|
||||
TBD:
|
||||
|
||||
* Chain of Responsibility: ADT + pattern matching the ADT (at least the distpatch variant)
|
||||
|
||||
* Partial application
|
||||
|
||||
* Blockchain as Monadic chain of Actions
|
||||
|
||||
### Dependency Injection → Parameter Binding, Partial Application
|
||||
|
||||
> [...] Dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client's state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
|
||||
@ -2124,6 +2116,116 @@ BankAccount {accountNo = 5678, name = "Marjin Mejer", branch = "Reikjavik", bala
|
||||
|
||||
[Sourcecode for this section](https://github.com/thma/LtuPatternFactory/blob/master/src/Builder.hs)
|
||||
|
||||
## Specific functional programming patterns
|
||||
|
||||
The patterns presented in this section all stem from functional languages.
|
||||
That is, they have been first developed in functional languages like Scheme or Haskell and have later been adopted from other languages.
|
||||
|
||||
### Map Reduce
|
||||
|
||||
### Continuations
|
||||
|
||||
### Laziness
|
||||
|
||||
Here comes a short snippet from a Java program:
|
||||
|
||||
```java
|
||||
// a non-terminating computation aka _|_ or bottom
|
||||
private static Void bottom() {
|
||||
return bottom();
|
||||
}
|
||||
|
||||
// the K combinator, K x y returns x
|
||||
private static <A, B> A k(A x ,B y ) {
|
||||
return x;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// part 1
|
||||
if (true) {
|
||||
System.out.println("21 is only half the truth");
|
||||
} else {
|
||||
bottom();
|
||||
}
|
||||
|
||||
// part 2
|
||||
System.out.println(k (42, bottom()));
|
||||
}
|
||||
```
|
||||
|
||||
What is the expected output of running `main`?
|
||||
In part 1 we expect to see the text "21 is only half the truth" on the console. The else part of the `if` statement will never be executed (thus avoiding the endless loop of calling `bottom()`) as `true` is always true.
|
||||
|
||||
But what will happen in part 2?
|
||||
If the Java compiler would be clever it could determine that `k (x, y)` will never need to evaluate `y` as is always returns just `x`. In this case we should see a 42 printed to the console.
|
||||
|
||||
But Java being a strict language Method calls have eager evaluation semantics.
|
||||
So will just see a `StackOverflowError`...
|
||||
|
||||
In a non-strict (or lazy) language like Haskell this will work out much smoother:
|
||||
|
||||
```haskell
|
||||
-- | bottom, a computation which never completes successfully, aka as _|_
|
||||
bottom :: a
|
||||
bottom = bottom
|
||||
|
||||
-- | the K combinator which drop its second argument (k x y = x)
|
||||
k :: a -> b -> a
|
||||
k x _ = x
|
||||
|
||||
infinityDemo :: IO ()
|
||||
infinityDemo = do
|
||||
print $ k 21 undefined -- evaluating undefined would result in a runtime error
|
||||
print $ k 42 bottom -- evaluating botoom would result in an endless loop
|
||||
putStrLn ""
|
||||
```
|
||||
|
||||
Haskell being a non-strict language the arguments of `k` are not evaluated when calling the function.
|
||||
thus in `k 21 undefined` and `k 42 bottom` the second arguments `undefined` and `bottom` are simply dropped and never evaluated.
|
||||
|
||||
The Haskell laziness can sometimes be tricky to deal with but it has also some huge benefits when dealing with infinite data structures.
|
||||
|
||||
```haskell
|
||||
-- | a list of *all* natural numbers
|
||||
ints :: Num a => [a]
|
||||
ints = from 1
|
||||
where
|
||||
from n = n : from (n + 1)
|
||||
```
|
||||
|
||||
This is a recursive definition of a list of numbers starting with n=1 and adding a list starting at n+1.
|
||||
As this recursion has no termination criteria it will never terminate!
|
||||
|
||||
What will happen when we start to use `ints` in our code?
|
||||
|
||||
```haskell
|
||||
ghci> take 10 ints
|
||||
[1,2,3,4,5,6,7,8,9,10]
|
||||
```
|
||||
|
||||
In this case we have not been greedy and just asked for finite subset of ints. The Haskell runtime does not fully evaluate ints but just as many elements as we aked for.
|
||||
|
||||
These kind of generator functions (also known as [CAFs]((https://wiki.haskell.org/Constant_applicative_form)) or Constant Applicative Forms) can be very useful to define lazy streams of infinite data.
|
||||
|
||||
Haskell even provides some more syntactic sugar to ease the definitions of such CAFs. So for instance our `ints` function could be written as:
|
||||
|
||||
```haskell
|
||||
ghci> ints = [1..]
|
||||
ghci> take 10 ints
|
||||
[1,2,3,4,5,6,7,8,9,10]
|
||||
```
|
||||
|
||||
This feature is called *arithmetic sequences* and allows also to define regions and stepwitdth:
|
||||
|
||||
```haskell
|
||||
ghci> [2,4..20]
|
||||
[2,4,6,8,10,12,14,16,18,20]
|
||||
```
|
||||
|
||||
Another useful feature are *list comprehensions*.
|
||||
|
||||
### Function as a Service
|
||||
|
||||
## Conclusions
|
||||
|
||||
### Design Patterns are not limited to object oriented programming
|
||||
|
16
src/Combinators.hs
Normal file
16
src/Combinators.hs
Normal file
@ -0,0 +1,16 @@
|
||||
module Combinators where
|
||||
|
||||
k :: a -> b -> a
|
||||
k x _ = x
|
||||
|
||||
s :: (a -> b -> c) -> (a -> b) -> a -> c
|
||||
s x y z = x z (y z)
|
||||
|
||||
i :: a -> a
|
||||
i = s k k
|
||||
|
||||
combinatorDemo :: IO ()
|
||||
combinatorDemo = do
|
||||
putStrLn "SKI combinators\n"
|
||||
print $ s k k 3
|
||||
putStrLn ""
|
@ -1,15 +1,35 @@
|
||||
module Infinity where
|
||||
|
||||
pythagoreanTriples :: [(Int,Int,Int)]
|
||||
pythagoreanTriples = [ (a,b,c) |
|
||||
c <- [1..],
|
||||
b <- [1..c-1],
|
||||
a <- [1..b-1],
|
||||
a^2 + b^2 == c^2]
|
||||
|
||||
-- | a list of all integer pythagorean triples with a² + b² = c²
|
||||
pythagoreanTriples :: [(Int, Int, Int)]
|
||||
pythagoreanTriples = [ (a, b, c)
|
||||
| c <- [1 ..]
|
||||
, b <- [1 .. c-1]
|
||||
, a <- [1 .. b-1]
|
||||
, a^2 + b^2 == c^2
|
||||
]
|
||||
|
||||
-- | bottom, a computation which never completes successfully, aka as _|_
|
||||
bottom :: a
|
||||
bottom = bottom
|
||||
|
||||
-- | a CAF representing all integer numbers (https://wiki.haskell.org/Constant_applicative_form)
|
||||
ints :: Num a => [a]
|
||||
ints = from 1
|
||||
where
|
||||
from n = n : from (n + 1)
|
||||
|
||||
-- | the K combinator which drop its second argument
|
||||
k :: a -> b -> a
|
||||
k x _ = x
|
||||
|
||||
infinityDemo :: IO ()
|
||||
infinityDemo = do
|
||||
putStrLn "Infinite structures and computations -> laziness & list comprehension\n"
|
||||
print $ take 100 pythagoreanTriples
|
||||
putStrLn ""
|
||||
putStrLn
|
||||
"Infinite data structures and nonterminating computations -> laziness & list comprehension\n"
|
||||
print $ take 100 ints
|
||||
print $ take 10 [2,4 ..]
|
||||
print $ take 10 pythagoreanTriples
|
||||
print $ k "21 is just half the truth" undefined
|
||||
print $ k 42 bottom
|
||||
putStrLn ""
|
||||
|
@ -1,10 +1,13 @@
|
||||
module Main where
|
||||
|
||||
import AbstractFactory
|
||||
import Adapter
|
||||
import Builder
|
||||
import Coerce
|
||||
import Composite
|
||||
import DependencyInjection
|
||||
import Infinity
|
||||
import Interpreter
|
||||
import Iterator
|
||||
import JsonPersistence
|
||||
import NullObject
|
||||
@ -13,8 +16,6 @@ import Singleton
|
||||
import Strategy
|
||||
import TemplateMethod
|
||||
import Visitor
|
||||
import Interpreter
|
||||
import Infinity
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
@ -33,4 +34,4 @@ main = do
|
||||
jsonPersistenceDemo
|
||||
demoDI
|
||||
interpreterDemo
|
||||
infinityDemo
|
||||
infinityDemo
|
||||
|
Loading…
Reference in New Issue
Block a user