-
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.
- Loading branch information
Manuel Bärenz
committed
Jan 18, 2024
1 parent
1485c3b
commit 58e04db
Showing
4 changed files
with
198 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
{-# LANGUAGE CPP #-} | ||
|
||
-- Disabling formatter and linter because it would fail on the syntax error otherwise. | ||
#ifndef __HLINT__ | ||
{- FOURMOLU_DISABLE -} | ||
|
||
-- Start reading here | ||
-- vvvvvvvvvvvvvvvvvv | ||
|
||
{- | Count everything nicer. | ||
The last problem got quite verbose, and fiddling around with nested tuples isn't fun. | ||
Fortunately, Haskell has a language extension that provides very useful syntax | ||
for data flow constructs like signal functions! | ||
It is called "arrow notation", and you can read a bit more about it here: https://www.haskell.org/arrows/. | ||
Have a look how the code can be cleaned up with it. | ||
-} | ||
module Koan where | ||
|
||
-- text | ||
import Data.Text qualified as Text (length, words) | ||
|
||
-- rhine | ||
import FRP.Rhine hiding (currentInput) | ||
|
||
-- | Compute the sum of all input numbers so far, including the current one. | ||
sumClSF :: (Monad m, Num a) => ClSF m cl a a | ||
sumClSF = feedback 0 $ arr aggregator | ||
where | ||
aggregator :: (Num a) => (a, a) -> (a, a) | ||
aggregator (currentInput, currentSum) = | ||
let | ||
nextSum = currentInput + currentSum | ||
in | ||
(nextSum, nextSum) | ||
|
||
-- | Print the number of total words and characters so far. | ||
printAllCounts :: ClSF IO StdinClock () () | ||
-- proc is a keyword. Think of it like a lambda expression! | ||
-- But why does GHC spit out a nasty parse error here? | ||
-- Read through the following to find out! | ||
printAllCounts = proc () -> do | ||
-- This is nearly like do notation, except it also has syntax for input, the -<. | ||
|
||
-- /------/--- Everything left from a <- is the output _signal_ of a signal function. | ||
-- | | It is a value that can depend on the current tick of the clock. | ||
-- | | | ||
-- | | /--- Signal functions can be used between <- and -<. | ||
-- | | | | ||
-- | | | /--- This is the input to the signal function. (tagS needs none.) | ||
-- | | | | | ||
-- v v v v | ||
userInput <- tagS -< () | ||
|
||
-- We can apply ordinary functions to signals. | ||
let wordCount = length $ Text.words userInput | ||
charCount = Text.length userInput + 1 | ||
|
||
lineCount <- count @Int -< () | ||
|
||
-- Signals can be inputs to signal functions. | ||
-- This way we can aggregate signals. | ||
totalWordCount <- sumClSF -< wordCount | ||
totalCharCount <- sumClSF -< charCount | ||
|
||
-- If a signal function has trivial output (), the <- is not needed. | ||
arrMCl print -< lineCount | ||
arrMCl print -< totalWordCount | ||
arrMCl print -< _ -- Which one is missing here? | ||
|
||
-- As you've seen, arrow notation introduces two new syntactic constructions, | ||
-- the proc keyword an the -< operator. | ||
-- You need to turn on a GHC language extension so that they can be parsed! | ||
-- Can you uncomment the following line, and move to the top of the file? | ||
-- {-# LANGUAGE Arrows #-} | ||
|
||
main :: IO () | ||
main = flow $ printAllCounts @@ StdinClock | ||
|
||
-- Ignore the next line ;) | ||
#endif |
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,71 @@ | ||
{-# LANGUAGE Arrows #-} | ||
|
||
{- | Count everything nicer. | ||
The last problem got quite verbose, and fiddling around with nested tuples isn't fun. | ||
Fortunately, Haskell has a language extension that provides very useful syntax | ||
for data flow constructs like signal functions! | ||
It is called "arrow notation", and you can read a bit more about it here: https://www.haskell.org/arrows/. | ||
Have a look how the code can be cleaned up with it. | ||
-} | ||
module Koan where | ||
|
||
-- text | ||
import Data.Text qualified as Text (length, words) | ||
|
||
-- rhine | ||
import FRP.Rhine hiding (currentInput) | ||
|
||
-- | Compute the sum of all input numbers so far, including the current one. | ||
sumClSF :: (Monad m, Num a) => ClSF m cl a a | ||
sumClSF = feedback 0 $ arr aggregator | ||
where | ||
aggregator :: (Num a) => (a, a) -> (a, a) | ||
aggregator (currentInput, currentSum) = | ||
let | ||
nextSum = currentInput + currentSum | ||
in | ||
(nextSum, nextSum) | ||
|
||
-- | Print the number of total words and characters so far. | ||
printAllCounts :: ClSF IO StdinClock () () | ||
-- proc is a keyword. Think of it like a lambda expression! | ||
-- But why does GHC spit out a nasty parse error here? | ||
-- Read through the following to find out! | ||
printAllCounts = proc () -> do | ||
-- This is nearly like do notation, except it also has syntax for input, the -<. | ||
|
||
-- /------/--- Everything left from a <- is the output _signal_ of a signal function. | ||
-- v v It is a value that can depend on the current tick of the clock. | ||
-- v v | ||
-- v v /--- Signal functions can be used between <- and -<. | ||
-- v v v | ||
-- v v v /--- This is the input to the signal function. (tagS needs none.) | ||
-- v v v v | ||
-- v v v v | ||
userInput <- tagS -< () | ||
|
||
-- We can apply ordinary functions to signals. | ||
let wordCount = length $ Text.words userInput | ||
charCount = Text.length userInput + 1 | ||
|
||
lineCount <- count @Int -< () | ||
|
||
-- Signals can be inputs to signal functions. | ||
-- This way we can aggregate signals. | ||
totalWordCount <- sumClSF -< wordCount | ||
totalCharCount <- sumClSF -< charCount | ||
|
||
-- If a signal function has trivial output (), the <- is not needed. | ||
arrMCl print -< lineCount | ||
arrMCl print -< totalWordCount | ||
arrMCl print -< totalCharCount | ||
|
||
-- As you've seen, arrow notation introduces two new syntactic constructions, | ||
-- the proc keyword an the -< operator. | ||
-- You need to turn on a GHC language extension so that they can be parsed! | ||
-- Can you uncomment the following line, and move to the top of the file? | ||
-- {-# LANGUAGE Arrows #-} | ||
|
||
main :: IO () | ||
main = flow $ printAllCounts @@ 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 as 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 <$> [1, 2, 12, 2, 5, 22, 3, 6, 27]) -> [] | ||
_ -> | ||
[ "The program produced output, but it wasn't quite right." | ||
, "It received the following input:" | ||
] | ||
++ testLines | ||
++ ["And it returned:"] | ||
++ output |
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