-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from turion/more_koans
More koans
- Loading branch information
Showing
41 changed files
with
637 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{- | Count the words. | ||
With any clock, you can treat the 'Tag' like any other data. | ||
For example, you can apply any function the console input. | ||
-} | ||
module Koan where | ||
|
||
-- text | ||
import Data.Text (Text) | ||
import Data.Text as Text (words) | ||
|
||
-- rhine | ||
import FRP.Rhine | ||
|
||
-- | A line of user input. | ||
userInput :: ClSF IO StdinClock () Text | ||
userInput = tagS | ||
|
||
-- | Output the number of words of the line that was just entered. | ||
wordCount :: ClSF IO StdinClock () Int | ||
-- Do you remember how to convert a pure function into a ClSF? | ||
wordCount = userInput >-> _ (Text.words >>> length) | ||
|
||
-- | Print the number of words of the line that was just entered. | ||
printWordCount :: ClSF IO StdinClock () () | ||
printWordCount = wordCount >-> arrMCl print | ||
|
||
main :: IO () | ||
main = flow $ printWordCount @@ StdinClock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{- | Count the words. | ||
With any clock, you can treat the 'Tag' like any other data. | ||
For example, you can apply any function the console input. | ||
-} | ||
module Koan where | ||
|
||
-- text | ||
import Data.Text (Text) | ||
import Data.Text as Text (words) | ||
|
||
-- rhine | ||
import FRP.Rhine | ||
|
||
-- | A line of user input. | ||
userInput :: ClSF IO StdinClock () Text | ||
userInput = tagS | ||
|
||
-- | Output the number of words of the line that was just entered. | ||
wordCount :: ClSF IO StdinClock () Int | ||
-- Do you remember how to convert a pure function into a ClSF? | ||
wordCount = userInput >-> arr (Text.words >>> length) | ||
|
||
-- | Print the number of words of the line that was just entered. | ||
printWordCount :: ClSF IO StdinClock () () | ||
printWordCount = wordCount >-> arrMCl print | ||
|
||
main :: IO () | ||
main = flow $ printWordCount @@ StdinClock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module Main where | ||
|
||
-- text | ||
import Data.Text (Text) | ||
import Data.Text as Text (words) | ||
|
||
-- koan | ||
import Koan qualified (main) | ||
|
||
-- test-io | ||
import TestIO | ||
|
||
testLines :: [Text] | ||
testLines = | ||
[ "Hello Rhine" | ||
, "this is a" | ||
, "test" | ||
] | ||
|
||
main :: IO () | ||
main = testForSecondsInput 1 testLines Koan.main $ \output -> | ||
case output of | ||
[] -> ["Weird, your program didn't produce any output!"] | ||
_ | output == (tshow . length . Text.words <$> testLines) -> [] | ||
_ -> ["The program produced output, but the lines had the wrong lengths."] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{- | Count the lines. | ||
Signal functions can have information about the past by storing internal state. | ||
For example, we can count the number of lines that have been entered so far. | ||
-} | ||
module Koan where | ||
|
||
-- rhine | ||
import FRP.Rhine | ||
|
||
{- | The number of lines of input so far. | ||
The 'count' signal function has internal state, the current count. | ||
Every time it is called (because 'StdinClock' has ticked), | ||
the count is incremented and returned. | ||
-} | ||
lineCount :: ClSF IO StdinClock () Integer | ||
lineCount = count -- This is part of the library! | ||
|
||
-- | Print the number of the line that was just entered. | ||
printLineCount :: ClSF IO StdinClock () () | ||
printLineCount = lineCount >-> arrMCl print | ||
|
||
main :: IO () | ||
-- Recap: Do you remember how to make a 'Rhine' from a 'ClSF'? | ||
main = flow _ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{- | Count the lines. | ||
Signal functions can have information about the past by storing internal state. | ||
For example, we can count the number of lines that have been entered so far. | ||
-} | ||
module Koan where | ||
|
||
-- rhine | ||
import FRP.Rhine | ||
|
||
{- | The number of lines of input so far. | ||
The 'count' signal function has internal state, the current count. | ||
Every time it is called (because 'StdinClock' has ticked), | ||
the count is incremented and returned. | ||
-} | ||
lineCount :: ClSF IO StdinClock () Integer | ||
lineCount = count -- This is part of the library! | ||
|
||
-- | Print the number of the line that was just entered. | ||
printLineCount :: ClSF IO StdinClock () () | ||
printLineCount = lineCount >-> arrMCl print | ||
|
||
main :: IO () | ||
-- Recap: Do you remember how to make a 'Rhine' from a 'ClSF'? | ||
main = flow $ printLineCount @@ StdinClock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
module Main where | ||
|
||
-- text | ||
import Data.Text (Text) | ||
|
||
-- koan | ||
import Koan qualified (main) | ||
|
||
-- test-io | ||
import TestIO | ||
|
||
testLines :: [Text] | ||
testLines = | ||
[ "Hello Rhine" | ||
, "this is a" | ||
, "test" | ||
] | ||
|
||
main :: IO () | ||
main = testForSecondsInput 1 testLines Koan.main $ \output -> | ||
case output of | ||
[] -> ["Weird, your program didn't produce any output!"] | ||
_ | output == take (length testLines) (tshow @Int <$> [1 ..]) -> [] | ||
_ -> ["The program produced output, but it wasn't quite right."] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
{- | Count the all the words! | ||
There are a number of ways how you can create internal state yourself. | ||
One of them is 'feedback'. Let's look at its type signature: | ||
@ | ||
feedback :: s -> ClSF m (a, s) (b, s) -> ClSF m a b | ||
@ | ||
'feedback' takes an internal state @s@, | ||
and a signal function with two inputs and two outputs. | ||
The two inputs are @a@ and the current internal state @s@. | ||
The two outputs are @b@ and the modified internal state. | ||
'feedback' then hides this state, which is why at the end, | ||
the return type is @ClSF m a b@. | ||
If you call 'feedback', you can pass an arbitrary signal function as second argument. | ||
It allows you to use and modify the internal state. | ||
-} | ||
module Koan where | ||
|
||
-- text | ||
import Data.Text (Text) | ||
import Data.Text as Text (words) | ||
|
||
-- rhine | ||
import FRP.Rhine hiding (currentInput) | ||
|
||
-- | A line of user input. | ||
userInput :: ClSF IO StdinClock () Text | ||
userInput = tagS | ||
|
||
-- | Output the number of words of the line that was just entered. | ||
wordCount :: ClSF IO StdinClock () Int | ||
wordCount = userInput >-> arr (Text.words >>> length) | ||
|
||
{- | Compute the sum of all input numbers so far, including the current one. | ||
/----------/--/-- Let's solve this problem in general, for any number type, any monad and any clock. | ||
| | | | ||
v v v | ||
-} | ||
sumClSF :: (Monad m, Num a) => ClSF m cl a a | ||
sumClSF = | ||
feedback -- We're using internal state | ||
0 -- As long as no input has arrived, this is the internal state we start with | ||
$ arr aggregator -- No side effects or further state needed: We manipulate the state with a pure function. | ||
where | ||
aggregator :: (Num a) => (a, a) -> (a, a) | ||
aggregator (currentInput, currentSum) = | ||
let | ||
nextSum = _ -- What should be the state after a further line of input has arrived? | ||
in | ||
-- The missing part is the final output of the signal function. | ||
-- If we have summed up to a certain number, what should it be? | ||
(_, nextSum) | ||
|
||
-- | The number of words of input so far. | ||
totalWordCount :: ClSF IO StdinClock () Int | ||
totalWordCount = wordCount >-> sumClSF | ||
|
||
-- | Print the number of total words so far. | ||
printTotalWordCount :: ClSF IO StdinClock () () | ||
printTotalWordCount = totalWordCount >-> arrMCl print | ||
|
||
main :: IO () | ||
main = flow $ printTotalWordCount @@ StdinClock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
{- | Count the all the words! | ||
There are a number of ways how you can create internal state yourself. | ||
One of them is 'feedback'. Let's look at its type signature: | ||
@ | ||
feedback :: s -> ClSF m (a, s) (b, s) -> ClSF m a b | ||
@ | ||
'feedback' takes an internal state @s@, | ||
and a signal function with two inputs and two outputs. | ||
The two inputs are @a@ and the current internal state @s@. | ||
The two outputs are @b@ and the modified internal state. | ||
'feedback' then hides this state, which is why at the end, | ||
the return type is @ClSF m a b@. | ||
If you call 'feedback', you can pass an arbitrary signal function as second argument. | ||
It allows you to use and modify the internal state. | ||
-} | ||
module Koan where | ||
|
||
-- text | ||
import Data.Text (Text) | ||
import Data.Text as Text (words) | ||
|
||
-- rhine | ||
import FRP.Rhine hiding (currentInput) | ||
|
||
-- | A line of user input. | ||
userInput :: ClSF IO StdinClock () Text | ||
userInput = tagS | ||
|
||
-- | Output the number of words of the line that was just entered. | ||
wordCount :: ClSF IO StdinClock () Int | ||
wordCount = userInput >-> arr (Text.words >>> length) | ||
|
||
{- | Compute the sum of all input numbers so far, including the current one. | ||
/----------/--/-- Let's solve this problem in general, for any number type, any monad and any clock. | ||
| | | | ||
v v v | ||
-} | ||
sumClSF :: (Monad m, Num a) => ClSF m cl a a | ||
sumClSF = | ||
feedback -- We're using internal state | ||
0 -- As long as no input has arrived, this is the internal state we start with | ||
$ arr aggregator -- No side effects or further state needed: We manipulate the state with a pure function. | ||
where | ||
aggregator :: (Num a) => (a, a) -> (a, a) | ||
aggregator (currentInput, currentSum) = | ||
let | ||
nextSum = currentInput + currentSum -- What should be the state after a further line of input has arrived? | ||
in | ||
-- The missing part is the final output of the signal function. | ||
-- If we have summed up to a certain number, what should it be? | ||
(nextSum, nextSum) | ||
|
||
-- | The number of words of input so far. | ||
totalWordCount :: ClSF IO StdinClock () Int | ||
totalWordCount = wordCount >-> sumClSF | ||
|
||
-- | Print the number of total words so far. | ||
printTotalWordCount :: ClSF IO StdinClock () () | ||
printTotalWordCount = totalWordCount >-> arrMCl print | ||
|
||
main :: IO () | ||
main = flow $ printTotalWordCount @@ StdinClock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
module Main where | ||
|
||
-- text | ||
import Data.Text | ||
|
||
-- koan | ||
import Koan qualified (main) | ||
|
||
-- test-io | ||
import TestIO | ||
|
||
testLines :: [Text] | ||
testLines = | ||
[ "Hello Rhine" | ||
, "this is a" | ||
, "test" | ||
] | ||
|
||
main :: IO () | ||
main = testForSecondsInput 1 testLines Koan.main $ \output -> | ||
case output of | ||
[] -> ["Weird, your program didn't produce any output!"] | ||
_ | output == (tshow @Int <$> [2, 5, 6]) -> [] | ||
_ | ||
| output == (tshow @Int <$> [0, 2, 5]) -> | ||
[ "Your program seems to be counting the words, but only the past ones!" | ||
, "Can you make sure it includes the current line as well?" | ||
] | ||
_ | output == (tshow @Int <$> [2, 3, 1]) -> ["Your program seems to be counting the words, but it doesn't return their sum!"] | ||
_ -> ["The program produced output, but it wasn't quite right."] |
Oops, something went wrong.