diff --git a/chapter5.html b/chapter5.html index 9a37b9af..bd083046 100644 --- a/chapter5.html +++ b/chapter5.html @@ -432,6 +432,12 @@

Guards

> :type guard forall (m :: Type -> Type). Alternative m => Boolean -> m Unit +
+

The Unit type represents values with no computational content — the absence of a concrete meaningful value.

+

We often use Unit "wrapped" in a type constructor as the return type of a computation where we only care about the effects of the computation (or a "shape" of the result) and not some concrete value.

+

For example, the main function has the type Effect Unit. Main is an entry point to the project — we don't call it directly.

+

We'll explain what m in the type signature means in Chapter 6.

+

In our case, we can assume that PSCi reported the following type:

Boolean -> Array Unit
 
diff --git a/index.html b/index.html index 7abe7a30..70dac3da 100644 --- a/index.html +++ b/index.html @@ -205,7 +205,7 @@

License

The exercises are licensed under the MIT license.

Release

PureScript v0.15.12

-

Published on 2023-10-30

+

Published on 2023-11-11

diff --git a/print.html b/print.html index fd631ac2..6ff65290 100644 --- a/print.html +++ b/print.html @@ -206,7 +206,7 @@

License

The exercises are licensed under the MIT license.

Release

PureScript v0.15.12

-

Published on 2023-10-30

+

Published on 2023-11-11

Introduction

Functional JavaScript

Functional programming techniques have been making appearances in JavaScript for some time now:

@@ -1609,6 +1609,12 @@

Guards

> :type guard forall (m :: Type -> Type). Alternative m => Boolean -> m Unit +
+

The Unit type represents values with no computational content — the absence of a concrete meaningful value.

+

We often use Unit "wrapped" in a type constructor as the return type of a computation where we only care about the effects of the computation (or a "shape" of the result) and not some concrete value.

+

For example, the main function has the type Effect Unit. Main is an entry point to the project — we don't call it directly.

+

We'll explain what m in the type signature means in Chapter 6.

+

In our case, we can assume that PSCi reported the following type:

Boolean -> Array Unit
 
diff --git a/searchindex.js b/searchindex.js index e4cbb189..012eb4ec 100644 --- a/searchindex.js +++ b/searchindex.js @@ -1 +1 @@ -Object.assign(window.search, {"doc_urls":["../index.html#purescript-by-example","../index.html#status","../index.html#about-the-book","../index.html#license","../index.html#release","chapter1.html#introduction","chapter1.html#functional-javascript","chapter1.html#types-and-type-inference","chapter1.html#polyglot-web-programming","chapter1.html#prerequisites","chapter1.html#about-you","chapter1.html#how-to-read-this-book","chapter1.html#getting-help","chapter1.html#about-the-author","chapter1.html#acknowledgements","chapter2.html#getting-started","chapter2.html#chapter-goals","chapter2.html#environment-setup","chapter2.html#editor-support","chapter2.html#solving-exercises","chapter2.html#exercise","chapter2.html#solution","chapter2.html#exercises","chapter2.html#conclusion","chapter3.html#functions-and-records","chapter3.html#chapter-goals","chapter3.html#project-setup","chapter3.html#simple-types","chapter3.html#notes-on-indentation","chapter3.html#defining-our-types","chapter3.html#type-constructors-and-kinds","chapter3.html#quantified-types","chapter3.html#displaying-address-book-entries","chapter3.html#test-early-test-often","chapter3.html#creating-address-books","chapter3.html#curried-functions","chapter3.html#property-accessors","chapter3.html#querying-the-address-book","chapter3.html#infix-function-application","chapter3.html#function-composition","chapter3.html#exercises","chapter3.html#conclusion","chapter4.html#pattern-matching","chapter4.html#chapter-goals","chapter4.html#project-setup","chapter4.html#simple-pattern-matching","chapter4.html#simple-patterns","chapter4.html#guards","chapter4.html#exercises","chapter4.html#array-patterns","chapter4.html#record-patterns-and-row-polymorphism","chapter4.html#record-puns","chapter4.html#nested-patterns","chapter4.html#named-patterns","chapter4.html#exercises-1","chapter4.html#case-expressions","chapter4.html#pattern-match-failures-and-partial-functions","chapter4.html#algebraic-data-types","chapter4.html#using-adts","chapter4.html#exercises-2","chapter4.html#newtypes","chapter4.html#exercises-3","chapter4.html#a-library-for-vector-graphics","chapter4.html#computing-bounding-rectangles","chapter4.html#exercises-4","chapter4.html#conclusion","chapter5.html#recursion-maps-and-folds","chapter5.html#chapter-goals","chapter5.html#project-setup","chapter5.html#introduction","chapter5.html#recursion-on-arrays","chapter5.html#exercises","chapter5.html#maps","chapter5.html#infix-operators","chapter5.html#filtering-arrays","chapter5.html#exercises-1","chapter5.html#flattening-arrays","chapter5.html#array-comprehensions","chapter5.html#do-notation","chapter5.html#guards","chapter5.html#exercises-2","chapter5.html#folds","chapter5.html#tail-recursion","chapter5.html#accumulators","chapter5.html#prefer-folds-to-explicit-recursion","chapter5.html#exercises-3","chapter5.html#a-virtual-filesystem","chapter5.html#listing-all-files","chapter5.html#exercises-4","chapter5.html#conclusion","chapter6.html#type-classes","chapter6.html#chapter-goals","chapter6.html#project-setup","chapter6.html#show-me","chapter6.html#exercises","chapter6.html#common-type-classes","chapter6.html#eq","chapter6.html#ord","chapter6.html#field","chapter6.html#semigroups-and-monoids","chapter6.html#foldable","chapter6.html#functor-and-type-class-laws","chapter6.html#deriving-instances","chapter6.html#exercises-1","chapter6.html#type-class-constraints","chapter6.html#instance-dependencies","chapter6.html#exercises-2","chapter6.html#multi-parameter-type-classes","chapter6.html#functional-dependencies","chapter6.html#nullary-type-classes","chapter6.html#superclasses","chapter6.html#exercises-3","chapter6.html#a-type-class-for-hashes","chapter6.html#exercises-4","chapter6.html#conclusion","chapter7.html#applicative-validation","chapter7.html#chapter-goals","chapter7.html#project-setup","chapter7.html#generalizing-function-application","chapter7.html#lifting-arbitrary-functions","chapter7.html#the-applicative-type-class","chapter7.html#intuition-for-applicative","chapter7.html#more-effects","chapter7.html#combining-effects","chapter7.html#exercises","chapter7.html#applicative-validation-1","chapter7.html#regular-expression-validators","chapter7.html#exercises-1","chapter7.html#traversable-functors","chapter7.html#exercises-2","chapter7.html#applicative-functors-for-parallelism","chapter7.html#conclusion","chapter8.html#the-effect-monad","chapter8.html#chapter-goals","chapter8.html#project-setup","chapter8.html#monads-and-do-notation","chapter8.html#the-monad-type-class","chapter8.html#monad-laws","chapter8.html#identity-laws","chapter8.html#folding-with-monads","chapter8.html#monads-and-applicatives","chapter8.html#exercises","chapter8.html#native-effects","chapter8.html#side-effects-and-purity","chapter8.html#the-effect-monad-1","chapter8.html#exceptions","chapter8.html#mutable-state","chapter8.html#exercises-1","chapter8.html#dom-effects","chapter8.html#an-address-book-user-interface","chapter8.html#exercises-2","chapter8.html#conclusion","chapter9.html#asynchronous-effects","chapter9.html#chapter-goals","chapter9.html#project-setup","chapter9.html#asynchronous-javascript","chapter9.html#asynchronous-purescript","chapter9.html#exercises","chapter9.html#additional-aff-resources","chapter9.html#a-http-client","chapter9.html#exercises-1","chapter9.html#parallel-computations","chapter9.html#exercises-2","chapter9.html#conclusion","chapter10.html#the-foreign-function-interface","chapter10.html#chapter-goals","chapter10.html#project-setup","chapter10.html#a-disclaimer","chapter10.html#calling-javascript-from-purescript","chapter10.html#functions-of-multiple-arguments","chapter10.html#uncurried-functions","chapter10.html#a-note-about-uncurried-functions","chapter10.html#a-note-about-modern-javascript-syntax","chapter10.html#exercises","chapter10.html#passing-simple-types","chapter10.html#exercises-1","chapter10.html#beyond-simple-types","chapter10.html#defining-foreign-types","chapter10.html#exceptions","chapter10.html#exercises-2","chapter10.html#using-type-class-member-functions","chapter10.html#effectful-functions","chapter10.html#asynchronous-functions","chapter10.html#exercises-3","chapter10.html#json","chapter10.html#exercises-4","chapter10.html#address-book","chapter10.html#exercises-5","chapter10.html#conclusion","chapter10.html#addendum","chapter10.html#calling-purescript-from-javascript","chapter10.html#understanding-name-generation","chapter10.html#runtime-data-representation","chapter10.html#representing-adts","chapter10.html#representing-quantified-types","chapter10.html#representing-constrained-types","chapter10.html#exercises-6","chapter10.html#representing-side-effects","chapter11.html#monadic-adventures","chapter11.html#chapter-goals","chapter11.html#project-setup","chapter11.html#how-to-play-the-game","chapter11.html#the-state-monad","chapter11.html#exercises","chapter11.html#the-reader-monad","chapter11.html#exercises-1","chapter11.html#the-writer-monad","chapter11.html#exercises-2","chapter11.html#monad-transformers","chapter11.html#the-exceptt-monad-transformer","chapter11.html#monad-transformer-stacks","chapter11.html#exercises-3","chapter11.html#type-classes-to-the-rescue","chapter11.html#alternatives","chapter11.html#monad-comprehensions","chapter11.html#backtracking","chapter11.html#exercises-4","chapter11.html#the-rws-monad","chapter11.html#implementing-game-logic","chapter11.html#running-the-computation","chapter11.html#exercises-5","chapter11.html#handling-command-line-options","chapter11.html#exercises-6","chapter11.html#conclusion","chapter12.html#canvas-graphics","chapter12.html#chapter-goals","chapter12.html#project-setup","chapter12.html#simple-shapes","chapter12.html#putting-row-polymorphism-to-work","chapter12.html#exercises","chapter12.html#drawing-random-circles","chapter12.html#transformations","chapter12.html#preserving-the-context","chapter12.html#global-mutable-state","chapter12.html#exercises-1","chapter12.html#l-systems","chapter12.html#exercises-2","chapter12.html#conclusion","chapter13.html#generative-testing","chapter13.html#chapter-goals","chapter13.html#project-setup","chapter13.html#writing-properties","chapter13.html#improving-error-messages","chapter13.html#exercises","chapter13.html#testing-polymorphic-code","chapter13.html#exercises-1","chapter13.html#generating-arbitrary-data","chapter13.html#exercises-2","chapter13.html#testing-higher-order-functions","chapter13.html#writing-coarbitrary-instances","chapter13.html#testing-without-side-effects","chapter13.html#exercises-3","chapter13.html#conclusion","chapter14.html#domain-specific-languages","chapter14.html#chapter-goals","chapter14.html#project-setup","chapter14.html#an-html-data-type","chapter14.html#smart-constructors","chapter14.html#exercises","chapter14.html#phantom-types","chapter14.html#exercises-1","chapter14.html#the-free-monad","chapter14.html#interpreting-the-monad","chapter14.html#exercises-2","chapter14.html#extending-the-language","chapter14.html#exercises-3","chapter14.html#conclusion"],"index":{"documentStore":{"docInfo":{"0":{"body":43,"breadcrumbs":3,"title":2},"1":{"body":40,"breadcrumbs":2,"title":1},"10":{"body":80,"breadcrumbs":1,"title":0},"100":{"body":196,"breadcrumbs":3,"title":1},"101":{"body":233,"breadcrumbs":6,"title":4},"102":{"body":16,"breadcrumbs":4,"title":2},"103":{"body":122,"breadcrumbs":3,"title":1},"104":{"body":205,"breadcrumbs":5,"title":3},"105":{"body":100,"breadcrumbs":4,"title":2},"106":{"body":145,"breadcrumbs":3,"title":1},"107":{"body":177,"breadcrumbs":6,"title":4},"108":{"body":210,"breadcrumbs":4,"title":2},"109":{"body":146,"breadcrumbs":5,"title":3},"11":{"body":198,"breadcrumbs":3,"title":2},"110":{"body":141,"breadcrumbs":3,"title":1},"111":{"body":258,"breadcrumbs":3,"title":1},"112":{"body":427,"breadcrumbs":5,"title":3},"113":{"body":108,"breadcrumbs":3,"title":1},"114":{"body":70,"breadcrumbs":3,"title":1},"115":{"body":0,"breadcrumbs":4,"title":2},"116":{"body":88,"breadcrumbs":4,"title":2},"117":{"body":49,"breadcrumbs":4,"title":2},"118":{"body":187,"breadcrumbs":5,"title":3},"119":{"body":490,"breadcrumbs":5,"title":3},"12":{"body":128,"breadcrumbs":3,"title":2},"120":{"body":55,"breadcrumbs":5,"title":3},"121":{"body":142,"breadcrumbs":4,"title":2},"122":{"body":341,"breadcrumbs":4,"title":2},"123":{"body":297,"breadcrumbs":4,"title":2},"124":{"body":65,"breadcrumbs":3,"title":1},"125":{"body":465,"breadcrumbs":4,"title":2},"126":{"body":129,"breadcrumbs":5,"title":3},"127":{"body":65,"breadcrumbs":3,"title":1},"128":{"body":556,"breadcrumbs":4,"title":2},"129":{"body":185,"breadcrumbs":3,"title":1},"13":{"body":104,"breadcrumbs":2,"title":1},"130":{"body":144,"breadcrumbs":5,"title":3},"131":{"body":108,"breadcrumbs":3,"title":1},"132":{"body":0,"breadcrumbs":4,"title":2},"133":{"body":33,"breadcrumbs":4,"title":2},"134":{"body":33,"breadcrumbs":4,"title":2},"135":{"body":296,"breadcrumbs":4,"title":2},"136":{"body":214,"breadcrumbs":5,"title":3},"137":{"body":22,"breadcrumbs":4,"title":2},"138":{"body":140,"breadcrumbs":4,"title":2},"139":{"body":277,"breadcrumbs":4,"title":2},"14":{"body":47,"breadcrumbs":2,"title":1},"140":{"body":223,"breadcrumbs":4,"title":2},"141":{"body":188,"breadcrumbs":3,"title":1},"142":{"body":132,"breadcrumbs":4,"title":2},"143":{"body":87,"breadcrumbs":5,"title":3},"144":{"body":285,"breadcrumbs":4,"title":2},"145":{"body":170,"breadcrumbs":3,"title":1},"146":{"body":515,"breadcrumbs":4,"title":2},"147":{"body":75,"breadcrumbs":3,"title":1},"148":{"body":96,"breadcrumbs":4,"title":2},"149":{"body":1218,"breadcrumbs":6,"title":4},"15":{"body":0,"breadcrumbs":4,"title":2},"150":{"body":120,"breadcrumbs":3,"title":1},"151":{"body":94,"breadcrumbs":3,"title":1},"152":{"body":0,"breadcrumbs":4,"title":2},"153":{"body":28,"breadcrumbs":4,"title":2},"154":{"body":45,"breadcrumbs":4,"title":2},"155":{"body":74,"breadcrumbs":4,"title":2},"156":{"body":204,"breadcrumbs":4,"title":2},"157":{"body":40,"breadcrumbs":3,"title":1},"158":{"body":38,"breadcrumbs":5,"title":3},"159":{"body":116,"breadcrumbs":4,"title":2},"16":{"body":25,"breadcrumbs":4,"title":2},"160":{"body":13,"breadcrumbs":3,"title":1},"161":{"body":353,"breadcrumbs":4,"title":2},"162":{"body":99,"breadcrumbs":3,"title":1},"163":{"body":24,"breadcrumbs":3,"title":1},"164":{"body":0,"breadcrumbs":6,"title":3},"165":{"body":94,"breadcrumbs":5,"title":2},"166":{"body":58,"breadcrumbs":5,"title":2},"167":{"body":103,"breadcrumbs":4,"title":1},"168":{"body":193,"breadcrumbs":6,"title":3},"169":{"body":81,"breadcrumbs":6,"title":3},"17":{"body":32,"breadcrumbs":4,"title":2},"170":{"body":112,"breadcrumbs":5,"title":2},"171":{"body":136,"breadcrumbs":6,"title":3},"172":{"body":101,"breadcrumbs":7,"title":4},"173":{"body":20,"breadcrumbs":4,"title":1},"174":{"body":187,"breadcrumbs":6,"title":3},"175":{"body":20,"breadcrumbs":4,"title":1},"176":{"body":269,"breadcrumbs":6,"title":3},"177":{"body":176,"breadcrumbs":6,"title":3},"178":{"body":43,"breadcrumbs":4,"title":1},"179":{"body":81,"breadcrumbs":4,"title":1},"18":{"body":40,"breadcrumbs":4,"title":2},"180":{"body":147,"breadcrumbs":8,"title":5},"181":{"body":171,"breadcrumbs":5,"title":2},"182":{"body":206,"breadcrumbs":5,"title":2},"183":{"body":12,"breadcrumbs":4,"title":1},"184":{"body":550,"breadcrumbs":4,"title":1},"185":{"body":230,"breadcrumbs":4,"title":1},"186":{"body":546,"breadcrumbs":5,"title":2},"187":{"body":55,"breadcrumbs":4,"title":1},"188":{"body":74,"breadcrumbs":4,"title":1},"189":{"body":0,"breadcrumbs":4,"title":1},"19":{"body":139,"breadcrumbs":4,"title":2},"190":{"body":137,"breadcrumbs":6,"title":3},"191":{"body":87,"breadcrumbs":6,"title":3},"192":{"body":280,"breadcrumbs":6,"title":3},"193":{"body":224,"breadcrumbs":5,"title":2},"194":{"body":204,"breadcrumbs":6,"title":3},"195":{"body":96,"breadcrumbs":6,"title":3},"196":{"body":44,"breadcrumbs":4,"title":1},"197":{"body":173,"breadcrumbs":6,"title":3},"198":{"body":0,"breadcrumbs":4,"title":2},"199":{"body":32,"breadcrumbs":4,"title":2},"2":{"body":103,"breadcrumbs":2,"title":1},"20":{"body":15,"breadcrumbs":3,"title":1},"200":{"body":37,"breadcrumbs":4,"title":2},"201":{"body":198,"breadcrumbs":4,"title":2},"202":{"body":256,"breadcrumbs":4,"title":2},"203":{"body":68,"breadcrumbs":3,"title":1},"204":{"body":197,"breadcrumbs":4,"title":2},"205":{"body":119,"breadcrumbs":3,"title":1},"206":{"body":243,"breadcrumbs":4,"title":2},"207":{"body":79,"breadcrumbs":3,"title":1},"208":{"body":471,"breadcrumbs":4,"title":2},"209":{"body":226,"breadcrumbs":5,"title":3},"21":{"body":180,"breadcrumbs":3,"title":1},"210":{"body":326,"breadcrumbs":5,"title":3},"211":{"body":101,"breadcrumbs":3,"title":1},"212":{"body":237,"breadcrumbs":5,"title":3},"213":{"body":189,"breadcrumbs":3,"title":1},"214":{"body":112,"breadcrumbs":4,"title":2},"215":{"body":126,"breadcrumbs":3,"title":1},"216":{"body":63,"breadcrumbs":3,"title":1},"217":{"body":265,"breadcrumbs":4,"title":2},"218":{"body":364,"breadcrumbs":5,"title":3},"219":{"body":362,"breadcrumbs":4,"title":2},"22":{"body":55,"breadcrumbs":3,"title":1},"220":{"body":61,"breadcrumbs":3,"title":1},"221":{"body":301,"breadcrumbs":6,"title":4},"222":{"body":28,"breadcrumbs":3,"title":1},"223":{"body":100,"breadcrumbs":3,"title":1},"224":{"body":0,"breadcrumbs":4,"title":2},"225":{"body":16,"breadcrumbs":4,"title":2},"226":{"body":82,"breadcrumbs":4,"title":2},"227":{"body":248,"breadcrumbs":4,"title":2},"228":{"body":360,"breadcrumbs":6,"title":4},"229":{"body":111,"breadcrumbs":3,"title":1},"23":{"body":103,"breadcrumbs":3,"title":1},"230":{"body":139,"breadcrumbs":5,"title":3},"231":{"body":147,"breadcrumbs":3,"title":1},"232":{"body":135,"breadcrumbs":4,"title":2},"233":{"body":212,"breadcrumbs":5,"title":3},"234":{"body":46,"breadcrumbs":3,"title":1},"235":{"body":880,"breadcrumbs":4,"title":2},"236":{"body":219,"breadcrumbs":3,"title":1},"237":{"body":87,"breadcrumbs":3,"title":1},"238":{"body":0,"breadcrumbs":4,"title":2},"239":{"body":71,"breadcrumbs":4,"title":2},"24":{"body":0,"breadcrumbs":4,"title":2},"240":{"body":25,"breadcrumbs":4,"title":2},"241":{"body":186,"breadcrumbs":4,"title":2},"242":{"body":81,"breadcrumbs":5,"title":3},"243":{"body":37,"breadcrumbs":3,"title":1},"244":{"body":148,"breadcrumbs":5,"title":3},"245":{"body":42,"breadcrumbs":3,"title":1},"246":{"body":392,"breadcrumbs":5,"title":3},"247":{"body":35,"breadcrumbs":3,"title":1},"248":{"body":252,"breadcrumbs":6,"title":4},"249":{"body":156,"breadcrumbs":5,"title":3},"25":{"body":56,"breadcrumbs":4,"title":2},"250":{"body":105,"breadcrumbs":6,"title":4},"251":{"body":90,"breadcrumbs":3,"title":1},"252":{"body":53,"breadcrumbs":3,"title":1},"253":{"body":0,"breadcrumbs":6,"title":3},"254":{"body":119,"breadcrumbs":5,"title":2},"255":{"body":19,"breadcrumbs":5,"title":2},"256":{"body":169,"breadcrumbs":6,"title":3},"257":{"body":446,"breadcrumbs":5,"title":2},"258":{"body":40,"breadcrumbs":4,"title":1},"259":{"body":249,"breadcrumbs":5,"title":2},"26":{"body":112,"breadcrumbs":4,"title":2},"260":{"body":58,"breadcrumbs":4,"title":1},"261":{"body":348,"breadcrumbs":5,"title":2},"262":{"body":299,"breadcrumbs":5,"title":2},"263":{"body":27,"breadcrumbs":4,"title":1},"264":{"body":540,"breadcrumbs":5,"title":2},"265":{"body":87,"breadcrumbs":4,"title":1},"266":{"body":115,"breadcrumbs":4,"title":1},"27":{"body":269,"breadcrumbs":4,"title":2},"28":{"body":135,"breadcrumbs":4,"title":2},"29":{"body":98,"breadcrumbs":4,"title":2},"3":{"body":37,"breadcrumbs":2,"title":1},"30":{"body":152,"breadcrumbs":5,"title":3},"31":{"body":114,"breadcrumbs":4,"title":2},"32":{"body":116,"breadcrumbs":6,"title":4},"33":{"body":93,"breadcrumbs":5,"title":3},"34":{"body":179,"breadcrumbs":5,"title":3},"35":{"body":365,"breadcrumbs":4,"title":2},"36":{"body":67,"breadcrumbs":4,"title":2},"37":{"body":327,"breadcrumbs":5,"title":3},"38":{"body":485,"breadcrumbs":5,"title":3},"39":{"body":113,"breadcrumbs":4,"title":2},"4":{"body":6,"breadcrumbs":2,"title":1},"40":{"body":139,"breadcrumbs":3,"title":1},"41":{"body":61,"breadcrumbs":3,"title":1},"42":{"body":12,"breadcrumbs":4,"title":2},"43":{"body":70,"breadcrumbs":4,"title":2},"44":{"body":91,"breadcrumbs":4,"title":2},"45":{"body":189,"breadcrumbs":5,"title":3},"46":{"body":68,"breadcrumbs":4,"title":2},"47":{"body":101,"breadcrumbs":3,"title":1},"48":{"body":92,"breadcrumbs":3,"title":1},"49":{"body":154,"breadcrumbs":4,"title":2},"5":{"body":0,"breadcrumbs":2,"title":1},"50":{"body":204,"breadcrumbs":6,"title":4},"51":{"body":83,"breadcrumbs":4,"title":2},"52":{"body":67,"breadcrumbs":4,"title":2},"53":{"body":64,"breadcrumbs":4,"title":2},"54":{"body":54,"breadcrumbs":3,"title":1},"55":{"body":120,"breadcrumbs":4,"title":2},"56":{"body":249,"breadcrumbs":7,"title":5},"57":{"body":312,"breadcrumbs":5,"title":3},"58":{"body":189,"breadcrumbs":4,"title":2},"59":{"body":40,"breadcrumbs":3,"title":1},"6":{"body":167,"breadcrumbs":3,"title":2},"60":{"body":285,"breadcrumbs":3,"title":1},"61":{"body":30,"breadcrumbs":3,"title":1},"62":{"body":74,"breadcrumbs":5,"title":3},"63":{"body":105,"breadcrumbs":5,"title":3},"64":{"body":51,"breadcrumbs":3,"title":1},"65":{"body":85,"breadcrumbs":3,"title":1},"66":{"body":12,"breadcrumbs":6,"title":3},"67":{"body":49,"breadcrumbs":5,"title":2},"68":{"body":66,"breadcrumbs":5,"title":2},"69":{"body":150,"breadcrumbs":4,"title":1},"7":{"body":220,"breadcrumbs":4,"title":3},"70":{"body":142,"breadcrumbs":5,"title":2},"71":{"body":32,"breadcrumbs":4,"title":1},"72":{"body":81,"breadcrumbs":4,"title":1},"73":{"body":302,"breadcrumbs":5,"title":2},"74":{"body":45,"breadcrumbs":5,"title":2},"75":{"body":57,"breadcrumbs":4,"title":1},"76":{"body":142,"breadcrumbs":5,"title":2},"77":{"body":181,"breadcrumbs":5,"title":2},"78":{"body":193,"breadcrumbs":4,"title":1},"79":{"body":141,"breadcrumbs":4,"title":1},"8":{"body":115,"breadcrumbs":4,"title":3},"80":{"body":104,"breadcrumbs":4,"title":1},"81":{"body":258,"breadcrumbs":4,"title":1},"82":{"body":165,"breadcrumbs":5,"title":2},"83":{"body":148,"breadcrumbs":4,"title":1},"84":{"body":82,"breadcrumbs":7,"title":4},"85":{"body":50,"breadcrumbs":4,"title":1},"86":{"body":113,"breadcrumbs":5,"title":2},"87":{"body":148,"breadcrumbs":5,"title":2},"88":{"body":73,"breadcrumbs":4,"title":1},"89":{"body":42,"breadcrumbs":4,"title":1},"9":{"body":50,"breadcrumbs":2,"title":1},"90":{"body":0,"breadcrumbs":4,"title":2},"91":{"body":80,"breadcrumbs":4,"title":2},"92":{"body":50,"breadcrumbs":4,"title":2},"93":{"body":324,"breadcrumbs":3,"title":1},"94":{"body":36,"breadcrumbs":3,"title":1},"95":{"body":26,"breadcrumbs":5,"title":3},"96":{"body":43,"breadcrumbs":3,"title":1},"97":{"body":90,"breadcrumbs":3,"title":1},"98":{"body":109,"breadcrumbs":3,"title":1},"99":{"body":122,"breadcrumbs":4,"title":2}},"docs":{"0":{"body":"This repository contains a community fork of PureScript by Example by Phil Freeman, also known as \"the PureScript book\". This version differs from the original in that it has been updated so that the code and exercises work with up-to-date versions of the compiler, libraries, and tools. Some chapters have also been rewritten to showcase the latest features of the PureScript ecosystem. If you enjoyed the book or found it useful, please consider buying a copy of the original on Leanpub . Translations: 日本語 (Japanese)","breadcrumbs":"Foreword » PureScript by Example","id":"0","title":"PureScript by Example"},"1":{"body":"This book is being continuously updated as the language evolves, so please report any issues you discover with the material. We appreciate any feedback you have to share, even if it's as simple as pointing out a confusing section that we could make more beginner-friendly. Unit tests are also being added to each chapter so you can check if your answers to the exercises are correct. See #79 for the latest status on tests.","breadcrumbs":"Foreword » Status","id":"1","title":"Status"},"10":{"body":"I will assume that you are familiar with the basics of JavaScript. Any prior familiarity with common tools from the JavaScript ecosystem, such as NPM and Gulp, will be beneficial if you wish to customize the standard setup to your own needs, but such knowledge is not necessary. No prior knowledge of functional programming is required, but it certainly won't hurt. New ideas will be accompanied by practical examples, so you should be able to form an intuition for the concepts from the functional programming that we will use. Readers who are familiar with the Haskell programming language will recognize a lot of the ideas and syntax presented in this book because PureScript is heavily influenced by Haskell. However, those readers should understand that there are a number of important differences between PureScript and Haskell. It is not necessarily always appropriate to try to apply ideas from one language in the other, although many of the concepts presented here will have some interpretation in Haskell.","breadcrumbs":"Introduction » About You","id":"10","title":"About You"},"100":{"body":"If the Monoid type class identifies those types which act as the result of a fold, then the Foldable type class identifies those type constructors which can be used as the source of a fold. The Foldable type class is provided in the foldable-traversable package, which also contains instances for some standard containers such as arrays and Maybe. The type signatures for the functions belonging to the Foldable class are a little more complicated than the ones we've seen so far: class Foldable f where foldr :: forall a b. (a -> b -> b) -> b -> f a -> b foldl :: forall a b. (b -> a -> b) -> b -> f a -> b foldMap :: forall a m. Monoid m => (a -> m) -> f a -> m It is instructive to specialize to the case where f is the array type constructor. In this case, we can replace f a with Array a for any a, and we notice that the types of foldl and foldr become the types we saw when we first encountered folds over arrays. What about foldMap? Well, that becomes forall a m. Monoid m => (a -> m) -> Array a -> m. This type signature says that we can choose any type m for our result type, as long as that type is an instance of the Monoid type class. If we can provide a function that turns our array elements into values in that monoid, then we can accumulate over our array using the structure of the monoid and return a single value. Let's try out foldMap in PSCi: > import Data.Foldable > foldMap show [1, 2, 3, 4, 5]\n\"12345\" Here, we choose the monoid for strings, which concatenates strings together, and the show function, which renders an Int as a String. Then, passing in an array of integers, we see that the results of showing each integer have been concatenated into a single String. But arrays are not the only types that are foldable. foldable-traversable also defines Foldable instances for types like Maybe and Tuple, and other libraries like lists define Foldable instances for their own data types. Foldable captures the notion of an ordered container .","breadcrumbs":"Type Classes » Foldable","id":"100","title":"Foldable"},"101":{"body":"The Prelude also defines a collection of type classes that enable a functional style of programming with side-effects in PureScript: Functor, Applicative, and Monad. We will cover these abstractions later in the book, but for now, let's look at the definition of the Functor type class, which we have seen already in the form of the map function: class Functor f where map :: forall a b. (a -> b) -> f a -> f b The map function (and its alias <$>) allows a function to be \"lifted\" over a data structure. The precise definition of the word \"lifted\" here depends on the data structure in question, but we have already seen its behavior for some simple types: > import Prelude > map (\\n -> n < 3) [1, 2, 3, 4, 5]\n[true, true, false, false, false] > import Data.Maybe\n> import Data.String (length) > map length (Just \"testing\")\n(Just 7) How can we understand the meaning of the map function, when it acts on many different structures, each in a different way? Well, we can build an intuition that the map function applies the function it is given to each element of a container, and builds a new container from the results, with the same shape as the original. But how do we make this concept precise? Type class instances for Functor are expected to adhere to a set of laws , called the functor laws : map identity xs = xs map g (map f xs) = map (g <<< f) xs The first law is the identity law . It states that lifting the identity function (the function which returns its argument unchanged) over a structure just returns the original structure. This makes sense since the identity function does not modify its input. The second law is the composition law . It states that mapping one function over a structure and then mapping a second is the same as mapping the composition of the two functions over the structure. Whatever \"lifting\" means in the general sense, it should be true that any reasonable definition of lifting a function over a data structure should obey these rules. Many standard type classes come with their own set of similar laws. The laws given to a type class give structure to the functions of that type class and allow us to study its instances in generality. The interested reader can research the laws ascribed to the standard type classes that we have seen already.","breadcrumbs":"Type Classes » Functor and Type Class Laws","id":"101","title":"Functor and Type Class Laws"},"102":{"body":"Rather than writing instances manually, you can let the compiler do most of the work for you. Take a look at this Type Class Deriving guide . That information will help you solve the following exercises.","breadcrumbs":"Type Classes » Deriving Instances","id":"102","title":"Deriving Instances"},"103":{"body":"The following newtype represents a complex number: newtype Complex = Complex { real :: Number , imaginary :: Number } (Easy) Define a Show instance for Complex. Match the output format expected by the tests (e.g. 1.2+3.4i, 5.6-7.8i, etc.). (Easy) Derive an Eq instance for Complex. Note : You may instead write this instance manually, but why do more work if you don't have to? (Medium) Define a Semiring instance for Complex. Note : You can use wrap and over2 from Data.Newtype to create a more concise solution. If you do so, you will also need to import class Newtype from Data.Newtype and derive a Newtype instance for Complex. (Easy) Derive (via newtype) a Ring instance for Complex. Note : You may instead write this instance manually, but that's not as convenient. Here's the Shape ADT from the previous chapter: data Shape = Circle Point Number | Rectangle Point Number Number | Line Point Point | Text Point String (Medium) Derive (via Generic) a Show instance for Shape. How does the amount of code written and String output compare to showShape from the previous chapter? Hint : See the Deriving from Generic section of the Type Class Deriving guide.","breadcrumbs":"Type Classes » Exercises","id":"103","title":"Exercises"},"104":{"body":"Types of functions can be constrained by using type classes. Here is an example: suppose we want to write a function that tests if three values are equal, by using equality defined using an Eq type class instance. threeAreEqual :: forall a. Eq a => a -> a -> a -> Boolean\nthreeAreEqual a1 a2 a3 = a1 == a2 && a2 == a3 The type declaration looks like an ordinary polymorphic type defined using forall. However, there is a type class constraint Eq a, separated from the rest of the type by a double arrow =>. This type says that we can call threeAreEqual with any choice of type a, as long as there is an Eq instance available for a in one of the imported modules. Constrained types can contain several type class instances, and the types of the instances are not restricted to simple type variables. Here is another example which uses Ord and Show instances to compare two values: showCompare :: forall a. Ord a => Show a => a -> a -> String\nshowCompare a1 a2 | a1 < a2 = show a1 <> \" is less than \" <> show a2\nshowCompare a1 a2 | a1 > a2 = show a1 <> \" is greater than \" <> show a2\nshowCompare a1 a2 = show a1 <> \" is equal to \" <> show a2 Note that multiple constraints can be specified by using the => symbol multiple times, just like we specify curried functions of multiple arguments. But remember not to confuse the two symbols: a -> b denotes the type of functions from type a to type b, whereas a => b applies the constraint a to the type b. The PureScript compiler will try to infer constrained types when a type annotation is not provided. This can be useful if we want to use the most general type possible for a function. To see this, try using one of the standard type classes like Semiring in PSCi: > import Prelude > :type \\x -> x + x\nforall (a :: Type). Semiring a => a -> a Here, we might have annotated this function as Int -> Int or Number -> Number, but PSCi shows us that the most general type works for any Semiring, allowing us to use our function with both Ints and `Number.","breadcrumbs":"Type Classes » Type Class Constraints","id":"104","title":"Type Class Constraints"},"105":{"body":"Just as the implementation of functions can depend on type class instances using constrained types, so can the implementation of type class instances depend on other type class instances. This provides a powerful form of program inference, in which the implementation of a program can be inferred using its types. For example, consider the Show type class. We can write a type class instance to show arrays of elements, as long as we have a way to show the elements themselves: instance Show a => Show (Array a) where ... If a type class instance depends on multiple other instances, those instances should be grouped in parentheses and separated by commas on the left-hand side of the => symbol: instance (Show a, Show b) => Show (Either a b) where ... These two type class instances are provided in the prelude library. When the program is compiled, the correct type class instance for Show is chosen based on the inferred type of the argument to show. The selected instance might depend on many such instance relationships, but this complexity is not exposed to the developer.","breadcrumbs":"Type Classes » Instance Dependencies","id":"105","title":"Instance Dependencies"},"106":{"body":"(Easy) The following declaration defines a type of non-empty arrays of elements of type a: data NonEmpty a = NonEmpty a (Array a) Write an Eq instance for the type NonEmpty a that reuses the instances for Eq a and Eq (Array a). Note: you may instead derive the Eq instance. (Medium) Write a Semigroup instance for NonEmpty a by reusing the Semigroup instance for Array. (Medium) Write a Functor instance for NonEmpty. (Medium) Given any type a with an instance of Ord, we can add a new \"infinite\" value that is greater than any other value: data Extended a = Infinite | Finite a Write an Ord instance for Extended a that reuses the Ord instance for a. (Difficult) Write a Foldable instance for NonEmpty. Hint : reuse the Foldable instance for arrays. (Difficult) Given a type constructor f which defines an ordered container (and so has a Foldable instance), we can create a new container type that includes an extra element at the front: data OneMore f a = OneMore a (f a) The container OneMore f also has an ordering, where the new element comes before any element of f. Write a Foldable instance for OneMore f: instance Foldable f => Foldable (OneMore f) where ... (Medium) Write a dedupShapes :: Array Shape -> Array Shape function that removes duplicate Shapes from an array using the nubEq function. (Medium) Write a dedupShapesFast function which is the same as dedupShapes, but uses the more efficient nub function.","breadcrumbs":"Type Classes » Exercises","id":"106","title":"Exercises"},"107":{"body":"It's not the case that a type class can only take a single type as an argument. This is the most common case, but a type class can be parameterized by zero or more type arguments. Let's see an example of a type class with two type arguments. module Stream where import Data.Array as Array\nimport Data.Maybe (Maybe)\nimport Data.String.CodeUnits as String class Stream stream element where uncons :: stream -> Maybe { head :: element, tail :: stream } instance Stream (Array a) a where uncons = Array.uncons instance Stream String Char where uncons = String.uncons The Stream module defines a class Stream which identifies types that look like streams of elements, where elements can be pulled from the front of the stream using the uncons function. Note that the Stream type class is parameterized not only by the type of the stream itself, but also by its elements. This allows us to define type class instances for the same stream type but different element types. The module defines two type class instances: an instance for arrays, where uncons removes the head element of the array using pattern matching, and an instance for String, which removes the first character from a String. We can write functions that work over arbitrary streams. For example, here is a function that accumulates a result in some Monoid based on the elements of a stream: import Prelude\nimport Data.Monoid (class Monoid, mempty) foldStream :: forall l e m. Stream l e => Monoid m => (e -> m) -> l -> m\nfoldStream f list = case uncons list of Nothing -> mempty Just cons -> f cons.head <> foldStream f cons.tail Try using foldStream in PSCi for different types of Stream and different types of Monoid.","breadcrumbs":"Type Classes » Multi-Parameter Type Classes","id":"107","title":"Multi-Parameter Type Classes"},"108":{"body":"Multi-parameter type classes can be very useful but can easily lead to confusing types and even issues with type inference. As a simple example, consider writing a generic tail function on streams using the Stream class given above: genericTail xs = map _.tail (uncons xs) This gives a somewhat confusing error message: The inferred type forall stream a. Stream stream a => stream -> Maybe stream has type variables which are not mentioned in the body of the type. Consider adding a type annotation. The problem is that the genericTail function does not use the element type mentioned in the definition of the Stream type class, so that type is left unsolved. Worse still, we cannot even use genericTail by applying it to a specific type of stream: > map _.tail (uncons \"testing\") The inferred type forall a. Stream String a => Maybe String has type variables which are not mentioned in the body of the type. Consider adding a type annotation. Here, we might expect the compiler to choose the streamString instance. After all, a String is a stream of Chars, and cannot be a stream of any other type of elements. The compiler cannot make that deduction automatically or commit to the streamString instance. However, we can help the compiler by adding a hint to the type class definition: class Stream stream element | stream -> element where uncons :: stream -> Maybe { head :: element, tail :: stream } Here, stream -> element is called a functional dependency . A functional dependency asserts a functional relationship between the type arguments of a multi-parameter type class. This functional dependency tells the compiler that there is a function from stream types to (unique) element types, so if the compiler knows the stream type, then it can commit to the element type. This hint is enough for the compiler to infer the correct type for our generic tail function above: > :type genericTail\nforall (stream :: Type) (element :: Type). Stream stream element => stream -> Maybe stream > genericTail \"testing\"\n(Just \"esting\") Functional dependencies can be useful when designing certain APIs using multi-parameter type classes.","breadcrumbs":"Type Classes » Functional Dependencies","id":"108","title":"Functional Dependencies"},"109":{"body":"We can even define type classes with zero-type arguments! These correspond to compile-time assertions about our functions, allowing us to track the global properties of our code in the type system. An important example is the Partial class we saw earlier when discussing partial functions. Take, for example, the functions head and tail defined in Data.Array.Partial that allow us to get the head or tail of an array without wrapping them in a Maybe, so they can fail if the array is empty: head :: forall a. Partial => Array a -> a tail :: forall a. Partial => Array a -> Array a Note that there is no instance defined for the Partial type class! Doing so would defeat its purpose: attempting to use the head function directly will result in a type error: > head [1, 2, 3] No type class instance was found for Prim.Partial Instead, we can republish the Partial constraint for any functions making use of partial functions: secondElement :: forall a. Partial => Array a -> a\nsecondElement xs = head (tail xs) We've already seen the unsafePartial function, which allows us to treat a partial function as a regular function (unsafely). This function is defined in the Partial.Unsafe module: unsafePartial :: forall a. (Partial => a) -> a Note that the Partial constraint appears inside the parentheses on the left of the function arrow, but not in the outer forall. That is, unsafePartial is a function from partial values to regular values: > unsafePartial head [1, 2, 3]\n1 > unsafePartial secondElement [1, 2, 3]\n2","breadcrumbs":"Type Classes » Nullary Type Classes","id":"109","title":"Nullary Type Classes"},"11":{"body":"The chapters in this book are largely self-contained. A beginner with little functional programming experience would be well-advised, however, to work through the chapters in order. The first few chapters lay the groundwork required to understand the material later on in the book. A reader who is comfortable with the ideas of functional programming (especially one with experience in a strongly-typed language like ML or Haskell) will probably be able to gain a general understanding of the code in the later chapters of the book without reading the preceding chapters. Each chapter will focus on a single practical example, providing the motivation for any new ideas introduced. Code for each chapter is available from the book's GitHub repository . Some chapters will include code snippets taken from the chapter's source code, but for a full understanding, you should read the source code from the repository alongside the material from the book. Longer sections will contain shorter snippets which you can execute in the interactive mode PSCi to test your understanding. Code samples will appear in a monospaced font as follows: module Example where import Effect.Console (log) main = log \"Hello, World!\" Commands which should be typed at the command line will be preceded by a dollar symbol: $ spago build Usually, these commands will be tailored to Linux/Mac OS users, so Windows users may need to make small changes, such as modifying the file separator or replacing shell built-ins with their Windows equivalents. Commands which should be typed at the PSCi interactive mode prompt will be preceded by an angle bracket: > 1 + 2\n3 Each chapter will contain exercises labelled with their difficulty level. It is strongly recommended that you attempt the exercises in each chapter to fully understand the material. This book aims to provide an introduction to the PureScript language for beginners, but it is not the sort of book that provides a list of template solutions to problems. For beginners, this book should be a fun challenge, and you will get the most benefit if you read the material, attempt the exercises, and, most importantly of all, try to write some code of your own.","breadcrumbs":"Introduction » How to Read This Book","id":"11","title":"How to Read This Book"},"110":{"body":"Just as we can express relationships between type class instances by making an instance dependent on another instance, we can express relationships between type classes themselves using so-called superclasses . We say that one type class is a superclass of another if every instance of the second class is required to be an instance of the first, and we indicate a superclass relationship in the class definition by using a backwards facing double arrow ( <= ). We've already seen an example of superclass relationships : the Eq class is a superclass of Ord, and the Semigroup class is a superclass of Monoid. For every type class instance of the Ord class, there must be a corresponding Eq instance for the same type. This makes sense since, in many cases, when the compare function reports that two values are incomparable, we often want to use the Eq class to determine if they are equal. In general, it makes sense to define a superclass relationship when the laws for the subclass mention the superclass members. For example, for any pair of Ord and Eq instances, it is reasonable to assume that if two values are equal under the Eq instance, then the compare function should return EQ. In other words, a == b should be true exactly when compare a b evaluates to EQ. This relationship on the level of laws justifies the superclass relationship between Eq and Ord. Another reason to define a superclass relationship is when there is a clear \"is-a\" relationship between the two classes. That is, every member of the subclass is a member of the superclass as well.","breadcrumbs":"Type Classes » Superclasses","id":"110","title":"Superclasses"},"111":{"body":"(Medium) Define a partial function unsafeMaximum :: Partial => Array Int -> Int that finds the maximum of a non-empty array of integers. Test out your function in PSCi using unsafePartial. Hint : Use the maximum function from Data.Foldable. (Medium) The Action class is a multi-parameter type class that defines an action of one type on another: class Monoid m <= Action m a where act :: m -> a -> a An action is a function that describes how monoidal values are used to determine how to modify a value of another type. There are two laws for the Action type class: act mempty a = a act (m1 <> m2) a = act m1 (act m2 a) Applying an empty action is a no-op. And applying two actions in sequence is the same as applying the actions combined. That is, actions respect the operations defined by the Monoid class. For example, the natural numbers form a monoid under multiplication: newtype Multiply = Multiply Int instance Semigroup Multiply where append (Multiply n) (Multiply m) = Multiply (n * m) instance Monoid Multiply where mempty = Multiply 1 Write an instance that implements this action: instance Action Multiply Int where ... Remember, your instance must satisfy the laws listed above. (Difficult) There are multiple ways to implement an instance of Action Multiply Int. How many can you think of? PureScript does not allow multiple implementations of the same instance, so you will have to replace your original implementation. Note : the tests cover 4 implementations. (Medium) Write an Action instance that repeats an input string some number of times: instance Action Multiply String where ... Hint : Search Pursuit for a helper-function with the signature String -> Int -> String . Note that String might appear as a more generic type (such as Monoid). Does this instance satisfy the laws listed above? (Medium) Write an instance Action m a => Action m (Array a), where the action on arrays is defined by acting on each array element independently. (Difficult) Given the following newtype, write an instance for Action m (Self m), where the monoid m acts on itself using append: newtype Self m = Self m Note : The testing framework requires Show and Eq instances for the Self and Multiply types. You may either write these instances manually, or let the compiler handle this for you with derive newtype instance shorthand. (Difficult) Should the arguments of the multi-parameter type class Action be related by some functional dependency? Why or why not? Note : There is no test for this exercise.","breadcrumbs":"Type Classes » Exercises","id":"111","title":"Exercises"},"112":{"body":"In the last section of this chapter, we will use the lessons from the rest of the chapter to create a library for hashing data structures. Note that this library is for demonstration purposes only and is not intended to provide a robust hashing mechanism. What properties might we expect of a hash function? A hash function should be deterministic and map equal values to equal hash codes. A hash function should distribute its results approximately uniformly over some set of hash codes. The first property looks a lot like a law for a type class, whereas the second property is more along the lines of an informal contract and certainly would not be enforceable by PureScript's type system. However, this should provide the intuition for the following type class: newtype HashCode = HashCode Int instance Eq HashCode where eq (HashCode a) (HashCode b) = a == b hashCode :: Int -> HashCode\nhashCode h = HashCode (h `mod` 65535) class Eq a <= Hashable a where hash :: a -> HashCode with the associated law that a == b implies hash a == hash b. We'll spend the rest of this section building a library of instances and functions associated with the Hashable type class. We will need a way to combine hash codes in a deterministic way: combineHashes :: HashCode -> HashCode -> HashCode\ncombineHashes (HashCode h1) (HashCode h2) = hashCode (73 * h1 + 51 * h2) The combineHashes function will mix two hash codes and redistribute the result over the interval 0-65535. Let's write a function that uses the Hashable constraint to restrict the types of its inputs. One common task which requires a hashing function is to determine if two values hash to the same hash code. The hashEqual relation provides such a capability: hashEqual :: forall a. Hashable a => a -> a -> Boolean\nhashEqual = eq `on` hash This function uses the on function from Data.Function to define hash-equality in terms of equality of hash codes, and should read like a declarative definition of hash-equality: two values are \"hash-equal\" if they are equal after each value passed through the hash function. Let's write some Hashable instances for some primitive types. Let's start with an instance for integers. Since a HashCode is really just a wrapped integer, this is simple – we can use the hashCode helper function: instance Hashable Int where hash = hashCode We can also define a simple instance for Boolean values using pattern matching: instance Hashable Boolean where hash false = hashCode 0 hash true = hashCode 1 With an instance for hashing integers, we can create an instance for hashing Chars by using the toCharCode function from Data.Char: instance Hashable Char where hash = hash <<< toCharCode To define an instance for arrays, we can map the hash function over the elements of the array (if the element type is also an instance of Hashable) and then perform a left fold over the resulting hashes using the combineHashes function: instance Hashable a => Hashable (Array a) where hash = foldl combineHashes (hashCode 0) <<< map hash Notice how we build up instances using the simpler instances we have already written. Let's use our new Array instance to define an instance for Strings, by turning a String into an array of Chars: instance Hashable String where hash = hash <<< toCharArray How can we prove that these Hashable instances satisfy the type class law that we stated above? We need to make sure that equal values have equal hash codes. In cases like Int, Char, String, and Boolean, this is simple because there are no values of those types that are equal in the sense of Eq but not equal identically. What about some more interesting types? To prove the type class law for the Array instance, we can use induction on the length of the array. The only array with a length zero is []. Any two non-empty arrays are equal only if they have equal head elements and equal tails, by the definition of Eq on arrays. By the inductive hypothesis, the tails have equal hashes, and we know that the head elements have equal hashes if the Hashable a instance must satisfy the law. Therefore, the two arrays have equal hashes, and so the Hashable (Array a) obeys the type class law as well. The source code for this chapter includes several other examples of Hashable instances, such as instances for the Maybe and Tuple type.","breadcrumbs":"Type Classes » A Type Class for Hashes","id":"112","title":"A Type Class for Hashes"},"113":{"body":"(Easy) Use PSCi to test the hash functions for each of the defined instances. Note : There is no provided unit test for this exercise. (Medium) Write a function arrayHasDuplicates, which tests if an array has any duplicate elements based on both hash and value equality. First, check for hash equality with the hashEqual function, then check for value equality with == if a duplicate pair of hashes is found. Hint : the nubByEq function in Data.Array should make this task much simpler. (Medium) Write a Hashable instance for the following newtype which satisfies the type class law: newtype Hour = Hour Int instance Eq Hour where eq (Hour n) (Hour m) = mod n 12 == mod m 12 The newtype Hour and its Eq instance represent the type of integers modulo 12, so that 1 and 13 are identified as equal, for example. Prove that the type class law holds for your instance. (Difficult) Prove the type class laws for the Hashable instances for Maybe, Either and Tuple. Note : There is no test for this exercise.","breadcrumbs":"Type Classes » Exercises","id":"113","title":"Exercises"},"114":{"body":"In this chapter, we've been introduced to type classes , a type-oriented form of abstraction that enables powerful forms of code reuse. We've seen a collection of standard type classes from the PureScript standard libraries and defined our own library based on a type class for computing hash codes. This chapter also introduced type class laws, a technique for proving properties about code that uses type classes for abstraction. Type class laws are part of a larger subject called equational reasoning , in which the properties of a programming language and its type system are used to enable logical reasoning about its programs. This is an important idea and a theme that we will return to throughout the rest of the book.","breadcrumbs":"Type Classes » Conclusion","id":"114","title":"Conclusion"},"115":{"body":"","breadcrumbs":"Applicative Validation » Applicative Validation","id":"115","title":"Applicative Validation"},"116":{"body":"In this chapter, we will meet an important new abstraction – the applicative functor , described by the Applicative type class. Don't worry if the name sounds confusing – we will motivate the concept with a practical example – validating form data. This technique allows us to convert code which usually involves a lot of boilerplate checking into a simple, declarative description of our form. We will also meet another type class, Traversable, which describes traversable functors , and see how this concept also arises very naturally from solutions to real-world problems. The example code for this chapter will be a continuation of the address book example from Chapter 3. This time, we will extend our address book data types and write functions to validate values for those types. The understanding is that these functions could be used, for example, in a web user interface, to display errors to the user as part of a data entry form.","breadcrumbs":"Applicative Validation » Chapter Goals","id":"116","title":"Chapter Goals"},"117":{"body":"The source code for this chapter is defined in the files src/Data/AddressBook.purs and src/Data/AddressBook/Validation.purs. The project has a number of dependencies, many of which we have seen before. There are two new dependencies: control, which defines functions for abstracting control flow using type classes like Applicative. validation, which defines a functor for applicative validation , the subject of this chapter. The Data.AddressBook module defines data types and Show instances for the types in our project and the Data.AddressBook.Validation module contains validation rules for those types.","breadcrumbs":"Applicative Validation » Project Setup","id":"117","title":"Project Setup"},"118":{"body":"To explain the concept of an applicative functor , let's consider the type constructor Maybe that we met earlier. The source code for this module defines a function address that has the following type: address :: String -> String -> String -> Address This function is used to construct a value of type Address from three strings: a street name, a city, and a state. We can apply this function easily and see the result in PSCi: > import Data.AddressBook > address \"123 Fake St.\" \"Faketown\" \"CA\"\n{ street: \"123 Fake St.\", city: \"Faketown\", state: \"CA\" } However, suppose we did not necessarily have a street, city, or state, and wanted to use the Maybe type to indicate a missing value in each of the three cases. In one case, we might have a missing city. If we try to apply our function directly, we will receive an error from the type checker: > import Data.Maybe\n> address (Just \"123 Fake St.\") Nothing (Just \"CA\") Could not match type Maybe String with type String Of course, this is an expected type error – address takes strings as arguments, not values of type Maybe String. However, it is reasonable to expect that we should be able to \"lift\" the address function to work with optional values described by the Maybe type. In fact, we can, and the Control.Apply provides the function lift3 function which does exactly what we need: > import Control.Apply\n> lift3 address (Just \"123 Fake St.\") Nothing (Just \"CA\") Nothing In this case, the result is Nothing, because one of the arguments (the city) was missing. If we provide all three arguments using the Just constructor, then the result will contain a value as well: > lift3 address (Just \"123 Fake St.\") (Just \"Faketown\") (Just \"CA\") Just ({ street: \"123 Fake St.\", city: \"Faketown\", state: \"CA\" }) The name of the function lift3 indicates that it can be used to lift functions of 3 arguments. There are similar functions defined in Control.Apply for functions of other numbers of arguments.","breadcrumbs":"Applicative Validation » Generalizing Function Application","id":"118","title":"Generalizing Function Application"},"119":{"body":"So, we can lift functions with small numbers of arguments by using lift2, lift3, etc. But how can we generalize this to arbitrary functions? It is instructive to look at the type of lift3: > :type lift3\nforall (a :: Type) (b :: Type) (c :: Type) (d :: Type) (f :: Type -> Type). Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d In the Maybe example above, the type constructor f is Maybe, so that lift3 is specialized to the following type: forall a b c d. (a -> b -> c -> d) -> Maybe a -> Maybe b -> Maybe c -> Maybe d This type says that we can take any function with three arguments and lift it to give a new function whose argument and result types are wrapped with Maybe. Certainly, this is not possible for every type constructor f, so what is it about the Maybe type which allowed us to do this? Well, in specializing the type above, we removed a type class constraint on f from the Apply type class. Apply is defined in the Prelude as follows: class Functor f where map :: forall a b. (a -> b) -> f a -> f b class Functor f <= Apply f where apply :: forall a b. f (a -> b) -> f a -> f b The Apply type class is a subclass of Functor, and defines an additional function apply. As <$> was defined as an alias for map, the Prelude module defines <*> as an alias for apply. As we'll see, these two operators are often used together. Note that this apply is different than the apply from Data.Function (infixed as $). Luckily, infix notation is almost always used for the latter, so you don't need to worry about name collisions. The type of apply looks a lot like the type of map. The difference between map and apply is that map takes a function as an argument, whereas the first argument to apply is wrapped in the type constructor f. We'll see how this is used soon, but first, let's see how to implement the Apply type class for the Maybe type: instance Functor Maybe where map f (Just a) = Just (f a) map f Nothing = Nothing instance Apply Maybe where apply (Just f) (Just x) = Just (f x) apply _ _ = Nothing This type class instance says that we can apply an optional function to an optional value, and the result is defined only if both are defined. Now we'll see how map and apply can be used together to lift functions of an arbitrary number of arguments. For functions of one argument, we can use map directly. For functions of two arguments, we have a curried function g with type a -> b -> c, say. This is equivalent to the type a -> (b -> c), so we can apply map to g to get a new function of type f a -> f (b -> c) for any type constructor f with a Functor instance. Partially applying this function to the first lifted argument (of type f a), we get a new wrapped function of type f (b -> c). If we also have an Apply instance for f, we can then use apply to apply the second lifted argument (of type f b) to get our final value of type f c. Putting this all together, we see that if we have values x :: f a and y :: f b, then the expression (g <$> x) <*> y has type f c (remember, this expression is equivalent to apply (map g x) y). The precedence rules defined in the Prelude allow us to remove the parentheses: g <$> x <*> y. In general, we can use <$> on the first argument, and <*> for the remaining arguments, as illustrated here for lift3: lift3 :: forall a b c d f . Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d\nlift3 f x y z = f <$> x <*> y <*> z It is left as an exercise for the reader to verify the types involved in this expression. As an example, we can try lifting the address function over Maybe, directly using the <$> and <*> functions: > address <$> Just \"123 Fake St.\" <*> Just \"Faketown\" <*> Just \"CA\"\nJust ({ street: \"123 Fake St.\", city: \"Faketown\", state: \"CA\" }) > address <$> Just \"123 Fake St.\" <*> Nothing <*> Just \"CA\"\nNothing Try lifting some other functions of various numbers of arguments over Maybe in this way. Alternatively, applicative do notation can be used for the same purpose in a way that looks similar to the familiar do notation . Here is lift3 using applicative do notation . Note ado is used instead of do, and in is used on the final line to denote the yielded value: lift3 :: forall a b c d f . Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d\nlift3 f x y z = ado a <- x b <- y c <- z in f a b c","breadcrumbs":"Applicative Validation » Lifting Arbitrary Functions","id":"119","title":"Lifting Arbitrary Functions"},"12":{"body":"If you get stuck at any point, there are a number of resources available online for learning PureScript: The PureScript Discord server is a great place to chat about issues you may be having. The server is dedicated to chatting about PureScript The PureScript Discourse Forum is another good place to search for solutions to common problems. PureScript: Jordan's Reference is an alternative learning resource that goes into great depth. If a concept in this book is difficult to understand, consider reading the corresponding section in that reference. Pursuit is a searchable database of PureScript types and functions. Read Pursuit's help page to learn what kinds of searches you can do . The unofficial PureScript Cookbook provides answers via code to \"How do I do X?\"-type questions. The PureScript documentation repository collects articles and examples on a wide variety of topics written by PureScript developers and users. The PureScript website contains links to several learning resources, including code samples, videos, and other resources for beginners. Try PureScript! is a website that allows users to compile PureScript code in the web browser and contains several simple examples of code. If you prefer to learn by reading examples, the purescript , purescript-node , and purescript-contrib GitHub organizations contain plenty of examples of PureScript code.","breadcrumbs":"Introduction » Getting Help","id":"12","title":"Getting Help"},"120":{"body":"There is a related type class called Applicative, defined as follows: class Apply f <= Applicative f where pure :: forall a. a -> f a Applicative is a subclass of Apply and defines the pure function. pure takes a value and returns a value whose type has been wrapped with the type constructor f. Here is the Applicative instance for Maybe: instance Applicative Maybe where pure x = Just x If we think of applicative functors as functors that allow lifting of functions, then pure can be thought of as lifting functions of zero arguments.","breadcrumbs":"Applicative Validation » The Applicative Type Class","id":"120","title":"The Applicative Type Class"},"121":{"body":"Functions in PureScript are pure and do not support side-effects. Applicative functors allow us to work in larger \"programming languages\" which support some sort of side-effect encoded by the functor f. As an example, the functor Maybe represents the side effect of possibly-missing values. Some other examples include Either err, which represents the side effect of possible errors of type err, and the arrow functor r ->, which represents the side-effect of reading from a global configuration. For now, we'll only consider the Maybe functor. If the functor f represents this larger programming language with effects, then the Apply and Applicative instances allow us to lift values and function applications from our smaller programming language (PureScript) into the new language. pure lifts pure (side-effect free) values into the larger language; for functions, we can use map and apply as described above. This raises a question: if we can use Applicative to embed PureScript functions and values into this new language, then how is the new language any larger? The answer depends on the functor f. If we can find expressions of type f a which cannot be expressed as pure x for some x, then that expression represents a term which only exists in the larger language. When f is Maybe, an example is the expression Nothing: we cannot write Nothing as pure x for any x. Therefore, we can think of PureScript as having been enlarged to include the new term Nothing, which represents a missing value.","breadcrumbs":"Applicative Validation » Intuition for Applicative","id":"121","title":"Intuition for Applicative"},"122":{"body":"Let's see some more examples of lifting functions over different Applicative functors. Here is a simple example function defined in PSCi, which joins three names to form a full name: > import Prelude > fullName first middle last = last <> \", \" <> first <> \" \" <> middle > fullName \"Phillip\" \"A\" \"Freeman\"\nFreeman, Phillip A Suppose that this function forms the implementation of a (very simple!) web service with the three arguments provided as query parameters. We want to ensure that the user provided each of the three parameters, so we might use the Maybe type to indicate the presence or absence of a parameter. We can lift fullName over Maybe to create an implementation of the web service which checks for missing parameters: > import Data.Maybe > fullName <$> Just \"Phillip\" <*> Just \"A\" <*> Just \"Freeman\"\nJust (\"Freeman, Phillip A\") > fullName <$> Just \"Phillip\" <*> Nothing <*> Just \"Freeman\"\nNothing Or with applicative do : > import Data.Maybe > :paste…\n… ado\n… f <- Just \"Phillip\"\n… m <- Just \"A\"\n… l <- Just \"Freeman\"\n… in fullName f m l\n… ^D\n(Just \"Freeman, Phillip A\") … ado\n… f <- Just \"Phillip\"\n… m <- Nothing\n… l <- Just \"Freeman\"\n… in fullName f m l\n… ^D\nNothing Note that the lifted function returns Nothing if any of the arguments was Nothing. This is good because now we can send an error response back from our web service if the parameters are invalid. However, it would be better if we could indicate which field was incorrect in the response. Instead of lifting over Maybe, we can lift over Either String, which allows us to return an error message. First, let's write an operator to convert optional inputs into computations which can signal an error using Either String: > import Data.Either\n> :paste\n… withError Nothing err = Left err\n… withError (Just a) _ = Right a\n… ^D Note : In the Either err applicative functor, the Left constructor indicates an error, and the Right constructor indicates success. Now we can lift over Either String, providing an appropriate error message for each parameter: > :paste\n… fullNameEither first middle last =\n… fullName <$> (first `withError` \"First name was missing\")\n… <*> (middle `withError` \"Middle name was missing\")\n… <*> (last `withError` \"Last name was missing\")\n… ^D Or with applicative do : > :paste\n… fullNameEither first middle last = ado\n… f <- first `withError` \"First name was missing\"\n… m <- middle `withError` \"Middle name was missing\"\n… l <- last `withError` \"Last name was missing\"\n… in fullName f m l\n… ^D > :type fullNameEither\nMaybe String -> Maybe String -> Maybe String -> Either String String Now our function takes three optional arguments using Maybe, and returns either a String error message or a String result. We can try out the function with different inputs: > fullNameEither (Just \"Phillip\") (Just \"A\") (Just \"Freeman\")\n(Right \"Freeman, Phillip A\") > fullNameEither (Just \"Phillip\") Nothing (Just \"Freeman\")\n(Left \"Middle name was missing\") > fullNameEither (Just \"Phillip\") (Just \"A\") Nothing\n(Left \"Last name was missing\") In this case, we see the error message corresponding to the first missing field or a successful result if every field was provided. However, if we are missing multiple inputs, we still only see the first error: > fullNameEither Nothing Nothing Nothing\n(Left \"First name was missing\") This might be good enough, but if we want to see a list of all missing fields in the error, then we need something more powerful than Either String. We will see a solution later in this chapter.","breadcrumbs":"Applicative Validation » More Effects","id":"122","title":"More Effects"},"123":{"body":"As an example of working with applicative functors abstractly, this section will show how to write a function that generically combines side-effects encoded by an applicative functor f. What does this mean? Well, suppose we have a list of wrapped arguments of type f a for some a. That is, suppose we have a list of type List (f a). Intuitively, this represents a list of computations with side-effects tracked by f, each with return type a. If we could run all of these computations in order, we would obtain a list of results of type List a. However, we would still have side-effects tracked by f. That is, we expect to be able to turn something of type List (f a) into something of type f (List a) by \"combining\" the effects inside the original list. For any fixed list size n, there is a function of n arguments that builds a list of size n out of those arguments. For example, if n is 3, the function is \\x y z -> x : y : z : Nil. This function has type a -> a -> a -> List a. We can use the Applicative instance for List to lift this function over f, to get a function of type f a -> f a -> f a -> f (List a). But, since we can do this for any n, it makes sense that we should be able to perform the same lifting for any list of arguments. That means that we should be able to write a function combineList :: forall f a. Applicative f => List (f a) -> f (List a) This function will take a list of arguments, which possibly have side-effects, and return a single wrapped list, applying the side-effects of each. To write this function, we'll consider the length of the list of arguments. If the list is empty, then we do not need to perform any effects, and we can use pure to simply return an empty list: combineList Nil = pure Nil In fact, this is the only thing we can do! If the list is non-empty, then we have a head element, which is a wrapped argument of type f a, and a tail of type List (f a). We can recursively combine the effects in the tail, giving a result of type f (List a). We can then use <$> and <*> to lift the Cons constructor over the head and new tail: combineList (Cons x xs) = Cons <$> x <*> combineList xs Again, this was the only sensible implementation, based on the types we were given. We can test this function in PSCi, using the Maybe type constructor as an example: > import Data.List\n> import Data.Maybe > combineList (fromFoldable [Just 1, Just 2, Just 3])\n(Just (Cons 1 (Cons 2 (Cons 3 Nil)))) > combineList (fromFoldable [Just 1, Nothing, Just 2])\nNothing When specialized to Maybe, our function returns a Just only if every list element is Just; otherwise, it returns Nothing. This is consistent with our intuition of working in a larger language supporting optional values – a list of computations that produce optional results only has a result itself if every computation contained a result. But the combineList function works for any Applicative! We can use it to combine computations that possibly signal an error using Either err, or which read from a global configuration using r ->. We will see the combineList function again later when we consider Traversable functors.","breadcrumbs":"Applicative Validation » Combining Effects","id":"123","title":"Combining Effects"},"124":{"body":"(Medium) Write versions of the numeric operators +, -, *, and / which work with optional arguments (i.e., arguments wrapped in Maybe) and return a value wrapped in Maybe. Name these functions addMaybe, subMaybe, mulMaybe, and divMaybe. Hint : Use lift2. (Medium) Extend the above exercise to work with all Apply types (not just Maybe). Name these new functions addApply, subApply, mulApply, and divApply. (Difficult) Write a function combineMaybe which has type forall a f. Applicative f => Maybe (f a) -> f (Maybe a). This function takes an optional computation with side-effects and returns a side-effecting computation with an optional result.","breadcrumbs":"Applicative Validation » Exercises","id":"124","title":"Exercises"},"125":{"body":"The source code for this chapter defines several data types which might be used in an address book application. The details are omitted here, but the key functions exported by the Data.AddressBook module have the following types: address :: String -> String -> String -> Address phoneNumber :: PhoneType -> String -> PhoneNumber person :: String -> String -> Address -> Array PhoneNumber -> Person Where PhoneType is defined as an algebraic data type: data PhoneType = HomePhone | WorkPhone | CellPhone | OtherPhone These functions can construct a Person representing an address book entry. For example, the following value is defined in Data.AddressBook: examplePerson :: Person\nexamplePerson = person \"John\" \"Smith\" (address \"123 Fake St.\" \"FakeTown\" \"CA\") [ phoneNumber HomePhone \"555-555-5555\" , phoneNumber CellPhone \"555-555-0000\" ] Test this value in PSCi (this result has been formatted): > import Data.AddressBook > examplePerson\n{ firstName: \"John\"\n, lastName: \"Smith\"\n, homeAddress: { street: \"123 Fake St.\" , city: \"FakeTown\" , state: \"CA\" }\n, phones: [ { type: HomePhone , number: \"555-555-5555\" } , { type: CellPhone , number: \"555-555-0000\" } ]\n} We saw in a previous section how we could use the Either String functor to validate a data structure of type Person. For example, provided functions to validate the two names in the structure, we might validate the entire data structure as follows: nonEmpty1 :: String -> Either String String\nnonEmpty1 \"\" = Left \"Field cannot be empty\"\nnonEmpty1 value = Right value validatePerson1 :: Person -> Either String Person\nvalidatePerson1 p = person <$> nonEmpty1 p.firstName <*> nonEmpty1 p.lastName <*> pure p.homeAddress <*> pure p.phones Or with applicative do : validatePerson1Ado :: Person -> Either String Person\nvalidatePerson1Ado p = ado f <- nonEmpty1 p.firstName l <- nonEmpty1 p.lastName in person f l p.homeAddress p.phones In the first two lines, we use the nonEmpty1 function to validate a non-empty string. nonEmpty1 returns an error indicated with the Left constructor if its input is empty. Otherwise, it returns the value wrapped with the Right constructor. The final lines do not perform any validation but simply provide the address and phones fields to the person function as the remaining arguments. This function can be seen to work in PSCi, but it has a limitation that we have seen before: > validatePerson $ person \"\" \"\" (address \"\" \"\" \"\") []\n(Left \"Field cannot be empty\") The Either String applicative functor only provides the first error encountered. Given the input here, we would prefer to see two errors – one for the missing first name and a second for the missing last name. There is another applicative functor that the validation library provides. This functor is called V, and it can return errors in any semigroup . For example, we can use V (Array String) to return an array of Strings as errors, concatenating new errors onto the end of the array. The Data.AddressBook.Validation module uses the V (Array String) applicative functor to validate the data structures in the Data.AddressBook module. Here is an example of a validator taken from the Data.AddressBook.Validation module: type Errors = Array String nonEmpty :: String -> String -> V Errors String\nnonEmpty field \"\" = invalid [ \"Field '\" <> field <> \"' cannot be empty\" ]\nnonEmpty _ value = pure value lengthIs :: String -> Int -> String -> V Errors String\nlengthIs field len value | length value /= len = invalid [ \"Field '\" <> field <> \"' must have length \" <> show len ]\nlengthIs _ _ value = pure value validateAddress :: Address -> V Errors Address\nvalidateAddress a = address <$> nonEmpty \"Street\" a.street <*> nonEmpty \"City\" a.city <*> lengthIs \"State\" 2 a.state Or with applicative do : validateAddressAdo :: Address -> V Errors Address\nvalidateAddressAdo a = ado street <- nonEmpty \"Street\" a.street city <- nonEmpty \"City\" a.city state <- lengthIs \"State\" 2 a.state in address street city state validateAddress validates an Address structure. It checks that the street and city fields are non-empty and that the string in the state field has length 2. Notice how the nonEmpty and lengthIs validator functions both use the invalid function provided by the Data.Validation module to indicate an error. Since we are working in the Array String semigroup, invalid takes an array of strings as its argument. We can try this function in PSCi: > import Data.AddressBook\n> import Data.AddressBook.Validation > validateAddress $ address \"\" \"\" \"\"\n(invalid [ \"Field 'Street' cannot be empty\" , \"Field 'City' cannot be empty\" , \"Field 'State' must have length 2\" ]) > validateAddress $ address \"\" \"\" \"CA\"\n(invalid [ \"Field 'Street' cannot be empty\" , \"Field 'City' cannot be empty\" ]) This time, we receive an array of all validation errors.","breadcrumbs":"Applicative Validation » Applicative Validation","id":"125","title":"Applicative Validation"},"126":{"body":"The validatePhoneNumber function uses a regular expression to validate the form of its argument. The key is a matches validation function, which uses a Regex from the Data.String.Regex module to validate its input: matches :: String -> Regex -> String -> V Errors String\nmatches _ regex value | test regex value = pure value\nmatches field _ _ = invalid [ \"Field '\" <> field <> \"' did not match the required format\" ] Again, notice how pure is used to indicate successful validation, and invalid is used to signal an array of errors. validatePhoneNumber is built from the matches function in the same way as before: validatePhoneNumber :: PhoneNumber -> V Errors PhoneNumber\nvalidatePhoneNumber pn = phoneNumber <$> pure pn.\"type\" <*> matches \"Number\" phoneNumberRegex pn.number Or with applicative do : validatePhoneNumberAdo :: PhoneNumber -> V Errors PhoneNumber\nvalidatePhoneNumberAdo pn = ado tpe <- pure pn.\"type\" number <- matches \"Number\" phoneNumberRegex pn.number in phoneNumber tpe number Again, try running this validator against some valid and invalid inputs in PSCi: > validatePhoneNumber $ phoneNumber HomePhone \"555-555-5555\"\npure ({ type: HomePhone, number: \"555-555-5555\" }) > validatePhoneNumber $ phoneNumber HomePhone \"555.555.5555\"\ninvalid ([\"Field 'Number' did not match the required format\"])","breadcrumbs":"Applicative Validation » Regular Expression Validators","id":"126","title":"Regular Expression Validators"},"127":{"body":"(Easy) Write a regular expression stateRegex :: Regex to check that a string only contains two alphabetic characters. Hint : see the source code for phoneNumberRegex. (Medium) Write a regular expression nonEmptyRegex :: Regex to check that a string is not entirely whitespace. Hint : If you need help developing this regex expression, check out RegExr , which has a great cheatsheet and interactive test environment. (Medium) Write a function validateAddressImproved that is similar to validateAddress, but uses the above stateRegex to validate the state field and nonEmptyRegex to validate the street and city fields. Hint : see the source for validatePhoneNumber for an example of how to use matches.","breadcrumbs":"Applicative Validation » Exercises","id":"127","title":"Exercises"},"128":{"body":"The remaining validator is validatePerson, which combines the validators we have seen so far to validate an entire Person structure, including the following new validatePhoneNumbers function: validatePhoneNumbers :: String -> Array PhoneNumber -> V Errors (Array PhoneNumber)\nvalidatePhoneNumbers field [] = invalid [ \"Field '\" <> field <> \"' must contain at least one value\" ]\nvalidatePhoneNumbers _ phones = traverse validatePhoneNumber phones validatePerson :: Person -> V Errors Person\nvalidatePerson p = person <$> nonEmpty \"First Name\" p.firstName <*> nonEmpty \"Last Name\" p.lastName <*> validateAddress p.homeAddress <*> validatePhoneNumbers \"Phone Numbers\" p.phones or with applicative do validatePersonAdo :: Person -> V Errors Person\nvalidatePersonAdo p = ado firstName <- nonEmpty \"First Name\" p.firstName lastName <- nonEmpty \"Last Name\" p.lastName address <- validateAddress p.homeAddress numbers <- validatePhoneNumbers \"Phone Numbers\" p.phones in person firstName lastName address numbers validatePhoneNumbers uses a new function we haven't seen before – traverse. traverse is defined in the Data.Traversable module, in the Traversable type class: class (Functor t, Foldable t) <= Traversable t where traverse :: forall a b m. Applicative m => (a -> m b) -> t a -> m (t b) sequence :: forall a m. Applicative m => t (m a) -> m (t a) Traversable defines the class of traversable functors . The types of its functions might look a little intimidating, but validatePerson provides a good motivating example. Every traversable functor is both a Functor and Foldable (recall that a foldable functor was a type constructor that supported a fold operation, reducing a structure to a single value). In addition, a traversable functor can combine a collection of side-effects that depend on its structure. This may sound complicated, but let's simplify things by specializing to the case of arrays. The array type constructor is traversable, which means that there is a function: traverse :: forall a b m. Applicative m => (a -> m b) -> Array a -> m (Array b) Intuitively, given any applicative functor m, and a function which takes a value of type a and returns a value of type b (with side-effects tracked by m), we can apply the function to each element of an array of type Array a to obtain a result of type Array b (with side-effects tracked by m). Still not clear? Let's specialize further to the case where m is the V Errors applicative functor above. Now, we have a function of type traverse :: forall a b. (a -> V Errors b) -> Array a -> V Errors (Array b) This type signature says that if we have a validation function m for a type a, then traverse m is a validation function for arrays of type Array a. But that's exactly what we need to be able to validate the phones field of the Person data structure! We pass validatePhoneNumber to traverse to create a validation function that validates each element successively. In general, traverse walks over the elements of a data structure, performing computations with side-effects and accumulating a result. The type signature for Traversable's other function sequence might look more familiar: sequence :: forall a m. Applicative m => t (m a) -> m (t a) In fact, the combineList function that we wrote earlier is just a special case of the sequence function from the Traversable type class. Setting t to be the type constructor List, we recover the type of the combineList function: combineList :: forall f a. Applicative f => List (f a) -> f (List a) Traversable functors capture the idea of traversing a data structure, collecting a set of effectful computations, and combining their effects. In fact, sequence and traverse are equally important to the definition of Traversable – each can be implemented in terms of the other. This is left as an exercise for the interested reader. The Traversable instance for lists given in the Data.List module is: instance Traversable List where\n-- traverse :: forall a b m. Applicative m => (a -> m b) -> List a -> m (List b)\ntraverse _ Nil = pure Nil\ntraverse f (Cons x xs) = Cons <$> f x <*> traverse f xs (The actual definition was later modified to improve stack safety. You can read more about that change here .) In the case of an empty list, we can return an empty list using pure. If the list is non-empty, we can use the function f to create a computation of type f b from the head element. We can also call traverse recursively on the tail. Finally, we can lift the Cons constructor over the applicative functor m to combine the two results. But there are more examples of traversable functors than just arrays and lists. The Maybe type constructor we saw earlier also has an instance for Traversable. We can try it in PSCi: > import Data.Maybe\n> import Data.Traversable\n> import Data.AddressBook.Validation > traverse (nonEmpty \"Example\") Nothing\npure (Nothing) > traverse (nonEmpty \"Example\") (Just \"\")\ninvalid ([\"Field 'Example' cannot be empty\"]) > traverse (nonEmpty \"Example\") (Just \"Testing\")\npure ((Just \"Testing\")) These examples show that traversing the Nothing value returns Nothing with no validation, and traversing Just x uses the validation function to validate x. That is, traverse takes a validation function for type a and returns a validation function for Maybe a, i.e., a validation function for optional values of type a. Other traversable functors include Array, Tuple a, and Either a for any type a. Generally, most \"container\" data type constructors have Traversable instances. As an example, the exercises will include writing a Traversable instance for a type of binary trees.","breadcrumbs":"Applicative Validation » Traversable Functors","id":"128","title":"Traversable Functors"},"129":{"body":"(Easy) Write Eq and Show instances for the following binary tree data structure: data Tree a = Leaf | Branch (Tree a) a (Tree a) Recall from the previous chapter that you may either write these instances manually or let the compiler derive them. There are many \"correct\" formatting options for Show output. The test for this exercise expects the following whitespace style. This matches the default formatting of the generic show, so you only need to note this if you're planning on writing this instance manually. (Branch (Branch Leaf 8 Leaf) 42 Leaf) (Medium) Write a Traversable instance for Tree a, which combines side-effects left-to-right. Hint : There are some additional instance dependencies that need to be defined for Traversable. (Medium) Write a function traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b) that performs a pre-order traversal of the tree. This means the order of effect execution is root-left-right, instead of left-root-right as was done for the previous in-order traverse exercise. Hint : No additional instances need to be defined, and you don't need to call any of the functions defined earlier. Applicative do notation (ado) is the easiest way to write this function. (Medium) Write a function traversePostOrder that performs a post-order traversal of the tree where effects are executed left-right-root. (Medium) Create a new version of the Person type where the homeAddress field is optional (using Maybe). Then write a new version of validatePerson (renamed as validatePersonOptionalAddress) to validate this new Person. Hint : Use traverse to validate a field of type Maybe a. (Difficult) Write a function sequenceUsingTraverse which behaves like sequence, but is written in terms of traverse. (Difficult) Write a function traverseUsingSequence which behaves like traverse, but is written in terms of sequence.","breadcrumbs":"Applicative Validation » Exercises","id":"129","title":"Exercises"},"13":{"body":"I am the original developer of the PureScript compiler. I'm based in Los Angeles, California, and started programming at an early age in BASIC on an 8-bit personal computer, the Amstrad CPC. Since then, I have worked professionally in a variety of programming languages (including Java, Scala, C#, F#, Haskell and PureScript). Not long into my professional career, I began to appreciate functional programming and its connections with mathematics, and enjoyed learning functional concepts using the Haskell programming language. I started working on the PureScript compiler in response to my experience with JavaScript. I found myself using functional programming techniques that I had picked up in languages like Haskell, but wanted a more principled environment in which to apply them. Solutions at the time included various attempts to compile Haskell to JavaScript while preserving its semantics (Fay, Haste, GHCJS), but I was interested to see how successful I could be by approaching the problem from the other side – attempting to keep the semantics of JavaScript, while enjoying the syntax and type system of a language like Haskell. I maintain a blog , and can be reached on Twitter .","breadcrumbs":"Introduction » About the Author","id":"13","title":"About the Author"},"130":{"body":"In the discussion above, I chose the word \"combine\" to describe how applicative functors \"combine side-effects\". However, in all the examples given, it would be equally valid to say that applicative functors allow us to \"sequence\" effects. This would be consistent with the intuition that traversable functors provide a sequence function to combine effects in sequence based on a data structure. However, in general, applicative functors are more general than this. The applicative functor laws do not impose any ordering on the side-effects that their computations perform. It would be valid for an applicative functor to perform its side-effects in parallel. For example, the V validation functor returned an array of errors, but it would work just as well if we picked the Set semigroup, in which case it would not matter what order we ran the various validators. We could even run them in parallel over the data structure! As a second example, the parallel package provides a type class Parallel which supports parallel computations . Parallel provides a function parallel that uses some Applicative functor to compute the result of its input computation in parallel : f <$> parallel computation1 <*> parallel computation2 This computation would start computing values asynchronously using computation1 and computation2. When both results have been computed, they would be combined into a single result using the function f. We will see this idea in more detail when we apply applicative functors to the problem of callback hell later in the book. Applicative functors are a natural way to capture side-effects that can be combined in parallel.","breadcrumbs":"Applicative Validation » Applicative Functors for Parallelism","id":"130","title":"Applicative Functors for Parallelism"},"131":{"body":"In this chapter, we covered a lot of new ideas: We introduced the concept of an applicative functor which generalizes the idea of function application to type constructors that captures some notion of side-effect. We saw how applicative functors solved the problem of validating data structures and how by switching the applicative functor, we could change from reporting a single error to reporting all errors across a data structure. We met the Traversable type class, which encapsulates the idea of a traversable functor , or a container whose elements can be used to combine values with side-effects. Applicative functors are an interesting abstraction that provides neat solutions to a number of problems. We will see them a few more times throughout the book. In this case, the validation applicative functor provided a way to write validators in a declarative style, allowing us to define what our validators should validate and not how they should perform that validation. In general, we will see that applicative functors are a useful tool for the design of domain specific languages . In the next chapter, we will see a related idea, the class of monads , and extend our address book example to run in the browser!","breadcrumbs":"Applicative Validation » Conclusion","id":"131","title":"Conclusion"},"132":{"body":"","breadcrumbs":"The Effect Monad » The Effect Monad","id":"132","title":"The Effect Monad"},"133":{"body":"In the last chapter, we introduced applicative functors, an abstraction we used to deal with side-effects : optional values, error messages, and validation. This chapter will introduce another abstraction for dealing with side-effects more expressively: monads . The goal of this chapter is to explain why monads are a useful abstraction and their connection with do notation .","breadcrumbs":"The Effect Monad » Chapter Goals","id":"133","title":"Chapter Goals"},"134":{"body":"The project adds the following dependencies: effect – defines the Effect monad, the subject of the second half of the chapter. This dependency is often listed in every starter project (it's been a dependency of every chapter so far), so you'll rarely have to install it explicitly. react-basic-hooks – a web framework we will use for our Address Book app.","breadcrumbs":"The Effect Monad » Project Setup","id":"134","title":"Project Setup"},"135":{"body":"Do notation was first introduced when we covered array comprehensions . Array comprehensions provide syntactic sugar for the concatMap function from the Data.Array module. Consider the following example. Suppose we throw two dice and want to count the number of ways in which we can score a total of n. We could do this using the following non-deterministic algorithm: Choose the value x of the first throw. Choose the value y of the second throw. If the sum of x and y is n, return the pair [x, y], else fail. Array comprehensions allow us to write this non-deterministic algorithm naturally: import Prelude import Control.Plus (empty)\nimport Data.Array ((..)) countThrows :: Int -> Array (Array Int)\ncountThrows n = do x <- 1 .. 6 y <- 1 .. 6 if x + y == n then pure [ x, y ] else empty We can see that this function works in PSCi: > import Test.Examples > countThrows 10\n[[4,6],[5,5],[6,4]] > countThrows 12\n[[6,6]] In the last chapter, we formed an intuition for the Maybe applicative functor, embedding PureScript functions into a larger programming language supporting optional values . In the same way, we can form an intuition for the array monad , embedding PureScript functions into a larger programming language supporting non-deterministic choice . Generally, a monad for some type constructor m provides a way to use do notation with values of type m a. Note that in the array comprehension above, every line contains a computation of type Array a for some type a. In general, every line of a do notation block will contain a computation of type m a for some type a and our monad m. The monad m must be the same on every line (i.e., we fix the side-effect), but the types a can differ (i.e., individual computations can have different result types). Here is another example of do notation, this time applied to the type constructor Maybe. Suppose we have some type XML representing XML nodes, and a function child :: XML -> String -> Maybe XML Which looks for a child element of a node and returns Nothing if no such element exists. In this case, we can look for a deeply-nested element using do notation. Suppose we wanted to read a user's city from a user profile that had been encoded as an XML document: userCity :: XML -> Maybe XML\nuserCity root = do prof <- child root \"profile\" addr <- child prof \"address\" city <- child addr \"city\" pure city The userCity function looks for a child element profile, an element address inside the profile element, and finally, an element city inside the address element. If any of these elements are missing, the return value will be Nothing. Otherwise, the return value is constructed using Just from the city node. Remember, the pure function in the last line is defined for every Applicative functor. Since pure is defined as Just for the Maybe applicative functor, it would be equally valid to change the last line to Just city.","breadcrumbs":"The Effect Monad » Monads and Do Notation","id":"135","title":"Monads and Do Notation"},"136":{"body":"The Monad type class is defined as follows: class Apply m <= Bind m where bind :: forall a b. m a -> (a -> m b) -> m b class (Applicative m, Bind m) <= Monad m The key function here is bind, defined in the Bind type class. Just like for the <$> and <*> operators in the Functor and Apply type classes, the Prelude defines an infix alias >>= for the bind function. The Monad type class extends Bind with the operations of the Applicative type class we've already seen. It will be useful to see some examples of the Bind type class. A sensible definition for Bind on arrays can be given as follows: instance Bind Array where bind xs f = concatMap f xs This explains the connection between array comprehensions and the concatMap function that has been alluded to before. Here is an implementation of Bind for the Maybe type constructor: instance Bind Maybe where bind Nothing _ = Nothing bind (Just a) f = f a This definition confirms the intuition that missing values are propagated through a do notation block. Let's see how the Bind type class is related to do notation. Consider a simple do notation block that starts by binding a value from the result of some computation: do value <- someComputation whatToDoNext Every time the PureScript compiler sees this pattern, it replaces the code with this: bind someComputation \\value -> whatToDoNext or, written infix: someComputation >>= \\value -> whatToDoNext The computation whatToDoNext is allowed to depend on value. If there are multiple binds involved, this rule is applied multiple times, starting from the top. For example, the userCity example that we saw earlier gets desugared as follows: userCity :: XML -> Maybe XML\nuserCity root = child root \"profile\" >>= \\prof -> child prof \"address\" >>= \\addr -> child addr \"city\" >>= \\city -> pure city Notably, code expressed using do notation is often much clearer than the equivalent code using the >>= operator. However, writing binds explicitly using >>= can often lead to opportunities to write code in point-free form – but the usual warnings about readability apply.","breadcrumbs":"The Effect Monad » The Monad Type Class","id":"136","title":"The Monad Type Class"},"137":{"body":"The Monad type class comes equipped with three laws, called the monad laws . These tell us what we can expect from sensible implementations of the Monad type class. It is simplest to explain these laws using do notation.","breadcrumbs":"The Effect Monad » Monad Laws","id":"137","title":"Monad Laws"},"138":{"body":"The right-identity law is the simplest of the three laws. It tells us that we can eliminate a call to pure if it is the last expression in a do notation block: do x <- expr pure x The right-identity law says that this is equivalent to just expr. The left-identity law states that we can eliminate a call to pure if it is the first expression in a do notation block: do x <- pure y next This code is equivalent to next, after the name x has been replaced with the expression y. The last law is the associativity law . It tells us how to deal with nested do notation blocks. It states that the following piece of code: c1 = do y <- do x <- m1 m2 m3 is equivalent to this code: c2 = do x <- m1 y <- m2 m3 Each of these computations involves three monadic expressions m1, m2, and m3. In each case, the result of m1 is eventually bound to the name x, and the result of m2 is bound to the name y. In c1, the two expressions m1 and m2 are grouped into their own do notation block. In c2, all three expressions m1, m2, and m3 appear in the same do notation block. The associativity law tells us that it is safe to simplify nested do notation blocks in this way. Note that by the definition of how do notation gets desugared into calls to bind, both of c1 and c2 are also equivalent to this code: c3 = do x <- m1 do y <- m2 m3","breadcrumbs":"The Effect Monad » Identity Laws","id":"138","title":"Identity Laws"},"139":{"body":"As an example of working with monads abstractly, this section will present a function that works with any type constructor in the Monad type class. This should solidify the intuition that monadic code corresponds to programming \"in a larger language\" with side-effects, and also illustrate the generality which programming with monads brings. The function we will write is called foldM. It generalizes the foldl function we met earlier to a monadic context. Here is its type signature: foldM :: forall m a b. Monad m => (a -> b -> m a) -> a -> List b -> m a\nfoldl :: forall a b. (a -> b -> a) -> a -> List b -> a Notice that this is the same as the type of foldl, except for the appearance of the monad m. Intuitively, foldM performs a fold over a list in some context supporting some set of side-effects. For example, if we picked m to be Maybe, then our fold would be allowed to fail by returning Nothing at any stage – every step returns an optional result, and the result of the fold is therefore also optional. If we picked m to be the Array type constructor, then every step of the fold would be allowed to return zero or more results, and the fold would proceed to the next step independently for each result. In the end, the set of results would consist of all folds over all possible paths. This corresponds to a traversal of a graph! To write foldM, we can simply break the input list into cases. If the list is empty, then to produce the result of type a, we only have one option: we have to return the second argument: foldM _ a Nil = pure a Note that we have to use pure to lift a into the monad m. What if the list is non-empty? In that case, we have a value of type a, a value of type b, and a function of type a -> b -> m a. If we apply the function, we obtain a monadic result of type m a. We can bind the result of this computation with a backwards arrow <-. It only remains to recurse on the tail of the list. The implementation is simple: foldM f a (b : bs) = do a' <- f a b foldM f a' bs Note that this implementation is almost identical to that of foldl on lists, except for do notation. We can define and test this function in PSCi. Here is an example – suppose we defined a \"safe division\" function on integers, which tested for division by zero and used the Maybe type constructor to indicate failure: safeDivide :: Int -> Int -> Maybe Int\nsafeDivide _ 0 = Nothing\nsafeDivide a b = Just (a / b) Then we can use foldM to express iterated safe division: > import Test.Examples\n> import Data.List (fromFoldable) > foldM safeDivide 100 (fromFoldable [5, 2, 2])\n(Just 5) > foldM safeDivide 100 (fromFoldable [2, 0, 4])\nNothing The foldM safeDivide function returns Nothing if a division by zero was attempted at any point. Otherwise, it returns the result of repeatedly dividing the accumulator, wrapped in the Just constructor.","breadcrumbs":"The Effect Monad » Folding With Monads","id":"139","title":"Folding With Monads"},"14":{"body":"I would like to thank the many contributors who helped PureScript to reach its current state. Without the huge collective effort which has been made on the compiler, tools, libraries, documentation, and tests, the project would certainly have failed. The PureScript logo which appears on the cover of this book was created by Gareth Hughes and is gratefully reused here under the terms of the Creative Commons Attribution 4.0 license . Finally, I would like to thank everyone who has given me feedback and corrections on the contents of this book.","breadcrumbs":"Introduction » Acknowledgements","id":"14","title":"Acknowledgements"},"140":{"body":"Every instance of the Monad type class is also an instance of the Apply type class, by virtue of the superclass relationship between the two classes. However, there is also an implementation of the Apply type class which comes \"for free\" for any instance of Monad, given by the ap function: ap :: forall m a b. Monad m => m (a -> b) -> m a -> m b\nap mf ma = do f <- mf a <- ma pure (f a) If m is a law-abiding member of the Monad type class, then there is a valid Apply instance for m given by ap. The interested reader can check that ap agrees with apply for the monads we have already encountered: Array, Maybe, and Either e. If every monad is also an applicative functor, then we should be able to apply our intuition for applicative functors to every monad. In particular, we can reasonably expect a monad to correspond, in some sense, to programming \"in a larger language\" augmented with some set of additional side-effects. We should be able to lift functions of arbitrary arities, using map and apply, into this new language. But monads allow us to do more than we could do with just applicative functors, and the key difference is highlighted by the syntax of do notation. Consider the userCity example again, in which we looked for a user's city in an XML document that encoded their user profile: userCity :: XML -> Maybe XML\nuserCity root = do prof <- child root \"profile\" addr <- child prof \"address\" city <- child addr \"city\" pure city Do notation allows the second computation to depend on the result prof of the first, and the third computation to depend on the result addr of the second, and so on. This dependence on previous values is not possible using only the interface of the Applicative type class. Try writing userCity using only pure and apply: you will see that it is impossible. Applicative functors only allow us to lift function arguments which are independent of each other, but monads allow us to write computations which involve more interesting data dependencies. In the last chapter, we saw that the Applicative type class can be used to express parallelism. This was precisely because the function arguments being lifted were independent of one another. Since the Monad type class allows computations to depend on the results of previous computations, the same does not apply – a monad has to combine its side-effects in sequence.","breadcrumbs":"The Effect Monad » Monads and Applicatives","id":"140","title":"Monads and Applicatives"},"141":{"body":"(Easy) Write a function third that returns the third element of an array with three or more elements. Your function should return an appropriate Maybe type. Hint: Look up the types of the head and tail functions from the Data.Array module in the arrays package. Use do notation with the Maybe monad to combine these functions. (Medium) Write a function possibleSums which uses foldM to determine all possible totals that could be made using a set of coins. The coins will be specified as an array which contains the value of each coin. Your function should have the following result: > possibleSums []\n[0] > possibleSums [1, 2, 10]\n[0,1,2,3,10,11,12,13] Hint : This function can be written as a one-liner using foldM. You might want to use the nub and sort functions to remove duplicates and sort the result. (Medium) Confirm that the ap function and the apply operator agree for the Maybe monad. Note: There are no tests for this exercise. (Medium) Verify that the monad laws hold for the Monad instance for the Maybe type, as defined in the maybe package. Note: There are no tests for this exercise. (Medium) Write a function filterM which generalizes the filter function on lists. Your function should have the following type signature: filterM :: forall m a. Monad m => (a -> m Boolean) -> List a -> m (List a) (Difficult) Every monad has a default Functor instance given by: map f a = do x <- a pure (f x) Use the monad laws to prove that for any monad, the following holds: lift2 f (pure a) (pure b) = pure (f a b) Where the Apply instance uses the ap function defined above. Recall that lift2 was defined as follows: lift2 :: forall f a b c. Apply f => (a -> b -> c) -> f a -> f b -> f c\nlift2 f a b = f <$> a <*> b Note: There are no tests for this exercise.","breadcrumbs":"The Effect Monad » Exercises","id":"141","title":"Exercises"},"142":{"body":"We will now look at one particular monad of central importance in PureScript – the Effect monad. The Effect monad is defined in the Effect module. It is used to manage so-called native side-effects. If you are familiar with Haskell, it is the equivalent of the IO monad. What are native side-effects? They are the side-effects that distinguish JavaScript expressions from idiomatic PureScript expressions, which typically are free from side-effects. Some examples of native effects are: Console IO Random number generation Exceptions Reading/writing mutable state And in the browser: DOM manipulation XMLHttpRequest / AJAX calls Interacting with a websocket Writing/reading to/from local storage We have already seen plenty of examples of \"non-native\" side-effects: Optional values, as represented by the Maybe data type Errors, as represented by the Either data type Multi-functions, as represented by arrays or lists Note that the distinction is subtle. It is true, for example, that an error message is a possible side-effect of a JavaScript expression in the form of an exception. In that sense, exceptions do represent native side-effects, and it is possible to represent them using Effect. However, error messages implemented using Either are not a side-effect of the JavaScript runtime, and so it is not appropriate to implement error messages in that style using Effect. So it is not the effect itself, which is native, but rather how it is implemented at runtime.","breadcrumbs":"The Effect Monad » Native Effects","id":"142","title":"Native Effects"},"143":{"body":"In a pure language like PureScript, one question presents itself: without side-effects, how can one write useful real-world code? The answer is that PureScript does not aim to eliminate side-effects but to represent them in such a way that pure computations can be distinguished from computations with side-effects in the type system. In this sense, the language is still pure. Values with side-effects have different types from pure values. As such, it is impossible to pass a side-effecting argument to a function, for example, and have side-effects performed unexpectedly. The only way side-effects managed by the Effect monad will be presented is to run a computation of type Effect a from JavaScript. The Spago build tool (and other tools) provide a shortcut by generating additional JavaScript to invoke the main computation when the application starts. main is required to be a computation in the Effect monad.","breadcrumbs":"The Effect Monad » Side-Effects and Purity","id":"143","title":"Side-Effects and Purity"},"144":{"body":"The Effect monad provides a well-typed API for computations with side-effects, while at the same time generating efficient JavaScript. Let's look at the return type of the familiar log function. Effect indicates that this function produces a native effect, console IO in this case. Unit indicates that no meaningful data is returned. You can think of Unit as analogous to the void keyword in other languages, such as C, Java, etc. log :: String -> Effect Unit Aside: You may encounter IDE suggestions for the more general (and more elaborately typed) log function from Effect.Class.Console. This is interchangeable with the one from Effect.Console when dealing with the basic Effect monad. Reasons for the more general version will become clearer after reading about \"Monad Transformers\" in the \"Monadic Adventures\" chapter. For the curious (and impatient), this works because there's a MonadEffect instance for Effect. log :: forall m. MonadEffect m => String -> m Unit Now let's consider an Effect that returns meaningful data. The random function from Effect.Random produces a random Number. random :: Effect Number Here's a full example program (found in test/Random.purs of this chapter's exercises folder). module Test.Random where import Prelude\nimport Effect (Effect)\nimport Effect.Random (random)\nimport Effect.Console (logShow) main :: Effect Unit\nmain = do n <- random logShow n Because Effect is a monad, we use do notation to unwrap the data it contains before passing this data on to the effectful logShow function. As a refresher, here's the equivalent code written using the bind operator: main :: Effect Unit\nmain = random >>= logShow Try running this yourself with: spago run --main Test.Random You should see a randomly chosen number between 0.0 and 1.0 printed to the console. Aside: spago run defaults to searching in the Main module for a main function. You may also specify an alternate module as an entry point with the --main flag, as in the above example. Just be sure that this alternate module also contains a main function. Note that it's also possible to generate \"random\" (technically pseudorandom) data without resorting to impure effectful code. We'll cover these techniques in the \"Generative Testing\" chapter. As mentioned previously, the Effect monad is of central importance to PureScript. The reason why it's central is that it is the conventional way to interoperate with PureScript's Foreign Function Interface, which provides the mechanism to execute a program and perform side effects. While it's desirable to avoid using the Foreign Function Interface, it's fairly critical to understand how it works and how to use it, so I recommend reading that chapter before doing any serious PureScript work. That said, the Effect monad is fairly simple. It has a few helper functions but doesn't do much except encapsulate side effects.","breadcrumbs":"The Effect Monad » The Effect Monad","id":"144","title":"The Effect Monad"},"145":{"body":"Let's examine a function from the node-fs package that involves two native side effects: reading mutable state and exceptions: readTextFile :: Encoding -> String -> Effect String If we attempt to read a file that does not exist: import Node.Encoding (Encoding(..))\nimport Node.FS.Sync (readTextFile) main :: Effect Unit\nmain = do lines <- readTextFile UTF8 \"iDoNotExist.md\" log lines We encounter the following exception: throw err; ^\nError: ENOENT: no such file or directory, open 'iDoNotExist.md'\n... errno: -2, syscall: 'open', code: 'ENOENT', path: 'iDoNotExist.md' To manage this exception gracefully, we can wrap the potentially problematic code in try to handle either outcome: main :: Effect Unit\nmain = do result <- try $ readTextFile UTF8 \"iDoNotExist.md\" case result of Right lines -> log $ \"Contents: \\n\" <> lines Left error -> log $ \"Couldn't open file. Error was: \" <> message error try runs an Effect and returns eventual exceptions as a Left value. If the computation succeeds, the result gets wrapped in a Right: try :: forall a. Effect a -> Effect (Either Error a) We can also generate our own exceptions. Here is an alternative implementation of Data.List.head that throws an exception if the list is empty rather than returning a Maybe value of Nothing. exceptionHead :: List Int -> Effect Int\nexceptionHead l = case l of x : _ -> pure x Nil -> throwException $ error \"empty list\" Note that the exceptionHead function is a somewhat impractical example, as it is best to avoid generating exceptions in PureScript code and instead use non-native effects such as Either and Maybe to manage errors and missing values.","breadcrumbs":"The Effect Monad » Exceptions","id":"145","title":"Exceptions"},"146":{"body":"There is another effect defined in the core libraries: the ST effect. The ST effect is used to manipulate mutable state. As pure functional programmers, we know that shared mutable state can be problematic. However, the ST effect uses the type system to restrict sharing in such a way that only safe local mutation is allowed. The ST effect is defined in the Control.Monad.ST module. To see how it works, we need to look at the types of its actions: new :: forall a r. a -> ST r (STRef r a) read :: forall a r. STRef r a -> ST r a write :: forall a r. a -> STRef r a -> ST r a modify :: forall r a. (a -> a) -> STRef r a -> ST r a new is used to create a new mutable reference cell of type STRef r a, which can be read using the read action and modified using the write and modify actions. The type a is the type of the value stored in the cell, and the type r is used to indicate a memory region (or heap ) in the type system. Here is an example. Suppose we want to simulate the movement of a particle falling under gravity by iterating a simple update function over many small time steps. We can do this by creating a mutable reference cell to hold the position and velocity of the particle, and then using a for loop to update the value stored in that cell: import Prelude import Control.Monad.ST.Ref (modify, new, read)\nimport Control.Monad.ST (ST, for, run) simulate :: forall r. Number -> Number -> Int -> ST r Number\nsimulate x0 v0 time = do ref <- new { x: x0, v: v0 } for 0 (time * 1000) \\_ -> modify ( \\o -> { v: o.v - 9.81 * 0.001 , x: o.x + o.v * 0.001 } ) ref final <- read ref pure final.x At the end of the computation, we read the final value of the reference cell and return the position of the particle. Note that even though this function uses a mutable state, it is still a pure function, so long as the reference cell ref is not allowed to be used by other program parts. We will see that this is exactly what the ST effect disallows. To run a computation with the ST effect, we have to use the run function: run :: forall a. (forall r. ST r a) -> a The thing to notice here is that the region type r is quantified inside the parentheses on the left of the function arrow. That means that whatever action we pass to run has to work with any region r whatsoever. However, once a reference cell has been created by new, its region type is already fixed, so it would be a type error to try to use the reference cell outside the code delimited by run. This allows run to safely remove the ST effect and turn simulate into a pure function! simulate' :: Number -> Number -> Int -> Number\nsimulate' x0 v0 time = run (simulate x0 v0 time) You can even try running this function in PSCi: > import Main > simulate' 100.0 0.0 0\n100.00 > simulate' 100.0 0.0 1\n95.10 > simulate' 100.0 0.0 2\n80.39 > simulate' 100.0 0.0 3\n55.87 > simulate' 100.0 0.0 4\n21.54 In fact, if we inline the definition of simulate at the call to run, as follows: simulate :: Number -> Number -> Int -> Number\nsimulate x0 v0 time = run do ref <- new { x: x0, v: v0 } for 0 (time * 1000) \\_ -> modify ( \\o -> { v: o.v - 9.81 * 0.001 , x: o.x + o.v * 0.001 } ) ref final <- read ref pure final.x Then the compiler will notice that the reference cell cannot escape its scope and can safely turn ref into a var. Here is the generated JavaScript for simulate inlined with run: var simulate = function (x0) { return function (v0) { return function (time) { return (function __do() { var ref = { value: { x: x0, v: v0 } }; Control_Monad_ST_Internal[\"for\"](0)(time * 1000 | 0)(function (v) { return Control_Monad_ST_Internal.modify(function (o) { return { v: o.v - 9.81 * 1.0e-3, x: o.x + o.v * 1.0e-3 }; })(ref); })(); return ref.value.x; })(); }; };\n}; Note that this resulting JavaScript is not as optimal as it could be. See this issue for more details. The above snippet should be updated once that issue is resolved. For comparison, this is the generated JavaScript of the non-inlined form: var simulate = function (x0) { return function (v0) { return function (time) { return function __do() { var ref = Control_Monad_ST_Internal[\"new\"]({ x: x0, v: v0 })(); Control_Monad_ST_Internal[\"for\"](0)(time * 1000 | 0)(function (v) { return Control_Monad_ST_Internal.modify(function (o) { return { v: o.v - 9.81 * 1.0e-3, x: o.x + o.v * 1.0e-3 }; })(ref); })(); var $$final = Control_Monad_ST_Internal.read(ref)(); return $$final.x; }; }; };\n}; The ST effect is a good way to generate short JavaScript when working with locally-scoped mutable state, especially when used together with actions like for, foreach, and while, which generate efficient loops.","breadcrumbs":"The Effect Monad » Mutable State","id":"146","title":"Mutable State"},"147":{"body":"(Medium) Rewrite the safeDivide function as exceptionDivide and throw an exception using throwException with the message \"div zero\" if the denominator is zero. (Medium) Write a function estimatePi :: Int -> Number that uses n terms of the Gregory Series to calculate an approximation of pi. Hints: You can pattern your answer like the definition of simulate above. You might need to convert an Int into a Number using toNumber :: Int -> Number from Data.Int. (Medium) Write a function fibonacci :: Int -> Int to compute the nth Fibonacci number, using ST to track the values of the previous two Fibonacci numbers. Using PSCi, compare the speed of your new ST-based implementation against the recursive implementation (fib) from Chapter 5.","breadcrumbs":"The Effect Monad » Exercises","id":"147","title":"Exercises"},"148":{"body":"In the final sections of this chapter, we will apply what we have learned about effects in the Effect monad to the problem of working with the DOM. There are several PureScript packages for working directly with the DOM or open-source DOM libraries. For example: web-dom provides type definitions and low-level interface implementations for the W3C DOM spec. web-html provides type definitions and low-level interface implementations for the W3C HTML5 spec. jquery is a set of bindings to the jQuery library. There are also PureScript libraries that build abstractions on top of these libraries, such as thermite builds on react react-basic-hooks builds on react-basic halogen provides a type-safe set of abstractions on top of a custom virtual DOM library. In this chapter, we will use the react-basic-hooks library to add a user interface to our address book application, but the interested reader is encouraged to explore alternative approaches.","breadcrumbs":"The Effect Monad » DOM Effects","id":"148","title":"DOM Effects"},"149":{"body":"Using the react-basic-hooks library, we will define our application as a React component . React components describe HTML elements in code as pure data structures, which are then efficiently rendered to the DOM. In addition, components can respond to events like button clicks. The react-basic-hooks library uses the Effect monad to describe how to handle these events. A full tutorial for the React library is well beyond the scope of this chapter, but the reader is encouraged to consult its documentation where needed. For our purposes, React will provide a practical example of the Effect monad. We are going to build a form that will allow a user to add a new entry into our address book. The form will contain text boxes for the various fields (first name, last name, city, state, etc.) and an area where validation errors will be displayed. As the user types text into the text boxes, the validation errors will be updated. To keep things simple, the form will have a fixed shape: the different phone number types (home, cell, work, other) will be expanded into separate text boxes. You can launch the web app from the exercises/chapter8 directory with the following commands: $ npm install\n$ npx spago build\n$ npx parcel src/index.html --open If development tools such as spago and parcel are installed globally, then the npx prefix may be omitted. You have likely already installed spago globally with npm i -g spago, and the same can be done for parcel. parcel should launch a browser window with our \"Address Book\" app. If you keep the parcel terminal open and rebuild with spago in another terminal, the page should automatically refresh with your latest edits. You can also configure automatic rebuilds (and therefore automatic page refresh) on file-save if you're using an editor that supports purs ide or are running pscid . In this Address Book app, you can enter some values into the form fields and see the validation errors printed onto the page. Let's explore how it works. The src/index.html file is minimal: \n Address Book
\n The \n The \n The \n The