Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» CS 330: Lecture 29 – Haskell IO

teaching machines

CS 330: Lecture 29 – Haskell IO

April 18, 2018 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
cs330, lectures, spring 2018.

Dear students,

Up till this point we haven’t written any standalone programs—none that get input from the user, none that generate output. We have looked at the purely functional side of Haskell, where time does not exist. Functions always produce the same value given the same inputs. No matter how many times you call it. There’s no notion of a counter that increments. No side effects. Who on Earth would want a language like this? I can think of a few:

  1. People who want to mathematically prove their programs are correct. Proving correctness is very hard to do if you allow RAM to get twiddled with through assignment statements—especially if pointers are involved.
  2. People who want their programs to run very quickly. Far fewer copies of data need to be made if you know it will never change. Functional programming promotes an unparallelled ethic of sharing. There’s no need to insulate one environment from another, because no damage can be done. Imagine if you could never catch a disease. You wouldn’t mind fraternizing with lepers.
  3. People who want their programs to easily be distributed across multiple machines. Imagine we have three computers processing a dataset. If one of them updates a variable that the others rely on, we have a hazard on our hands. How do we make sure the other machines get the update? We have to use some sort of synchronization to ensure consistency. But if we don’t allow updates, each machine can do its work without concern for the others.

That said, at some point our feet need to touch earth. Time is a thing, and state does change. Haskell does recognize this, but it becomes a different language. It becomes the language of actions. In Haskell, an action produces a side effect. The most obvious side effect is console output. We can use putStrLn to achieve this:

main = putStrLn "Goodbye, Pluto!"

If we want to issue several side effects, we can sequence them together in a do block:

main = do
  putStrLn "Thanks, Obama!"
  putStrLn "You're welcome!"

This should look more like what you’re used to, right?

Other kinds of side effects exist. Like opening a web browser with openBrowser, which is provided in module Web.Browser by the open-browser package:

import Web.Browser

main = do
  putStrLn "Thanks, Obama!"
  openBrowser "https://www.youtube.com/watch?v=79DijItQXMM"
  putStrLn "You're welcome!"

The documentation shows what kind of thing openBrowser returns:

openBrowser :: String -> IO Bool

Not a Bool, but an IO Bool. Haskell is serious about about keeping separate the world of side effects and the world of truth and identity. If you go get some data from the side effect world, it is tainted. Any result that a side effecting function gives back is packaged up in a capsule—an IO—that isolates the contagion.

Time is another side effect. We can effect the passage of time in Haskell with threadDelay. Here’s its interface:

threadDelay :: Int -> IO ()

We need to import Control.Concurrent to get access to threadDelay.

That set of parentheses is the empty tuple. Sometimes it’s called unit. You can think of it as void. This function is used purely for its side effects, giving back no result. But notice that even that non-result is tainted. Also note that it takes milliseconds, so that number has to be pretty big to be perceptable:

main = do
  putStrLn "Thanks, Obama!"
  threadDelay 3000000

Dealing with the running process’ environment is also done in the world of side effects. Suppose we want to get the user’s username. We can use functions from the System.Posix.User module. Its documentation tells us of the getLoginName function:

getLoginName :: IO String

It gives us back a tainted string. Hmm… Let’s try using it:

main = do
  putStrLn "Thanks, Obama!"
  threadDelay 3000000
  putStrLn $ getLoginName

This doesn’t work. putStrLn doesn’t want your tainted junk. It wants a pure string. What do we do? We must open the sealed capsule. The bind operator <- does that for us:

main = do
  putStrLn "Thanks, Obama!"
  threadDelay 3000000
  username <- getLoginName
  putStrLn $ "You're welcome, " ++ username ++ "!"

The bind operator effectively unwraps the data from its IO seal and adds a new variable into our execution environment. But here’s the thing: we can only bind like this inside another IO action. We can’t open up one of these capsules inside the world of truth and identity and statelessness. An IO action represents state, and Haskell makes it impossible to introduce state into the cleanroom that is pure functional programming.

What’s another side effect? How about input? There’s getLine. Can you guess what its signature is?

getLine :: IO String

To echo out the typed line, we do this:

main = do
  line <- getLine
  putStrLine line

What if we wanted an Int from the user? Well, we can write our own little helper that uses getLine and then parses it:

getInt :: IO Int
getInt = do
  line <- getLine
  ???

But then what do we do with it? Remember the show function? It turns values of many different types into a string representation. Here we have the string and we want to go in reverse. The opposite of show is read. However, since the view of the relationship in this direction is one-to-many, we must give read a clue about what type we want:

getInt :: IO Int
getInt = do
  line <- getLine
  let int = read line :: Int
  ???

The read function is not a stateful one, so we don’t need to bind. We can do a regular assignment. Except regular assignments inside IO actions must be preceded by the let keyword for reasons with which I’m not familiar.

Okay, now what about our return value? We have a beautiful and pure Int, but it was born in the world of taint. So, we must encapsulate it. That’s what return is for:

getInt :: IO Int
getInt = do
  line <- getLine
  let int = read line :: Int
  return int

return effectively has this type signature:

return :: a -> IO a

It’s not really like a return in imperative languages, as it doesn’t do anything with control flow. It just generates a tainted capsule. If that capsule is the last expression of the do block, it will get sent back to the caller, just as with pure functions.

Now we can write a function to get two numbers from the user and do something interesting with them:

main = do
  putStr "A: "
  a <- getInt
  putStr "B: "
  b <- getInt
  putStrLine $ show $ take a $ repeat b

Let’s deal with command-line parameters. This, like getting the username, involves communicating with the process, which is a stateful thing. We enter the world of side effects. The getArgs function in System.Environment has this signature:

getArgs :: IO [String]

Let’s extract out the first argument and turn into an Int:

main = do
  args <- getArgs              -- unwrap from the IO
  let arg0 = head arg          -- pull out just the first
  let n = read arg0 :: Int     -- parse the int

Erm… We should do something interesting here. Let’s print out all the factors of n. How can we do this in Haskell? Through filtering!

factors n = filter (\x -> mod n x == 0) [1..n]

main = do
  args <- getArgs              -- unwrap from the IO
  let arg0 = head arg          -- pull out just the first
  let n = read arg0 :: Int     -- parse the int
  putStrLn $ show $ factors n

We should remind ourselves why factors are important. Wasn’t it the Babylonians who chose to use 360 for the number of degrees in a circle because 360 had so many ways to divide it evenly? They made it far easier to split a pizza amongst groups of various sizes.

Let’s do one last example, this time with a loop. Let’s print the first command-line parameter vertically. For, like, acrostics and crossword puzzles and stuff. We still don’t have loops, but we can use recursion. Here’s our function that “loops”:

downer :: String -> IO ()
downer [] = return ()
downer (first:rest) = do
  putChar first
  putStrLn ""
  downer rest

And our main:

main = do
  args <- getArgs
  let arg0 = head args
  downer arg0

One of your homework problems requires a loop until the user gets a right answer. We can’t use pattern matching for that, so let’s rewrite this using a more flexible construct:

downer list = 
  if list == [] then
    return ()
  else
    do
      putChar first
      putStrLn ""
      downer rest

For the remainder of our time today, let’s solve problems from Open Kattis:

Next time we’ll look at file input!

Sincerely,

P.S. It’s time for a haiku!

I search “Russia prez”
I expect get some put-out
But I get Putin

P.P.S. Here’s the code we wrote together:

first.hs

import Web.Browser
import Control.Concurrent
import System.Posix.User

main = do
  putStrLn "Thanks, Obama!"
  -- openBrowser "https://www.youtube.com/watch?v=79DijItQXMM"
  threadDelay 3000000
  username <- getLoginName
  -- let a = 5
  -- let a = 6
  -- let username = "dayne"
  putStrLn $ "You're welcome, " ++ username ++ "!"

getter.hs

getInt :: IO Int
getInt = do
  line <- getLine
  let int = read line :: Int
  return int

main = do
  a <- getInt
  b <- getInt
  putStrLn $ show $ replicate b a

loop.hs

downer :: String -> IO ()
downer [] = return ()
downer (first : rest) = do
  putChar first
  putStrLn ""
  downer rest

main = do
  putStr "Title: "
  line <- getLine
  downer line