{"id":13543366,"url":"https://github.com/system-f/fp-course","last_synced_at":"2025-12-16T18:48:37.506Z","repository":{"id":6185162,"uuid":"7415520","full_name":"system-f/fp-course","owner":"system-f","description":"Functional Programming Course","archived":false,"fork":false,"pushed_at":"2025-10-07T10:56:27.000Z","size":4259,"stargazers_count":4215,"open_issues_count":16,"forks_count":1193,"subscribers_count":119,"default_branch":"master","last_synced_at":"2025-11-15T09:03:16.061Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/system-f.png","metadata":{"files":{"readme":"README.markdown","changelog":"changelog","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"support/Setup.hs","governance":null,"roadmap":null,"authors":null}},"created_at":"2013-01-03T00:13:31.000Z","updated_at":"2025-11-07T22:19:18.000Z","dependencies_parsed_at":"2024-01-06T13:09:26.016Z","dependency_job_id":"1d0c16c3-0d15-40da-a24e-bc0159858aaf","html_url":"https://github.com/system-f/fp-course","commit_stats":{"total_commits":1079,"total_committers":90,"mean_commits":"11.988888888888889","dds":0.4754402224281742,"last_synced_commit":"7a957a177ccdc9d91323c32d576351dcc1234f2f"},"previous_names":["data61/fp-course"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/system-f/fp-course","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/system-f%2Ffp-course","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/system-f%2Ffp-course/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/system-f%2Ffp-course/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/system-f%2Ffp-course/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/system-f","download_url":"https://codeload.github.com/system-f/fp-course/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/system-f%2Ffp-course/sbom","scorecard":{"id":863906,"data":{"date":"2025-08-18","repo":{"name":"github.com/system-f/fp-course","commit":"f2c32a8d543b4326785b2e31c4c7bb0f03084225"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":4.6,"checks":[{"name":"Code-Review","score":4,"reason":"Found 8/18 approved changesets -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":10,"reason":"project is fuzzed","details":["Info: HaskellPropertyBasedTesting integration found: src/Course/List.hs:27","Info: HaskellPropertyBasedTesting integration found: src/Course/ListZipper.hs:17","Info: HaskellPropertyBasedTesting integration found: src/Course/State.hs:18","Info: HaskellPropertyBasedTesting integration found: src/Course/State.hs:20","Info: HaskellPropertyBasedTesting integration found: src/Course/StateT.hs:21","Info: HaskellPropertyBasedTesting integration found: src/Course/Validation.hs:10","Info: HaskellPropertyBasedTesting integration found: src/Course/List.hs:27","Info: HaskellPropertyBasedTesting integration found: src/Course/ListZipper.hs:17","Info: HaskellPropertyBasedTesting integration found: src/Course/State.hs:18","Info: HaskellPropertyBasedTesting integration found: src/Course/State.hs:20","Info: HaskellPropertyBasedTesting integration found: src/Course/StateT.hs:21","Info: HaskellPropertyBasedTesting integration found: src/Course/Validation.hs:10"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 20 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-24T02:09:43.247Z","repository_id":6185162,"created_at":"2025-08-24T02:09:43.247Z","updated_at":"2025-08-24T02:09:43.247Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27769696,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-16T02:00:10.477Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T11:00:30.740Z","updated_at":"2025-12-16T18:48:37.499Z","avatar_url":"https://github.com/system-f.png","language":"Haskell","readme":"# Functional Programming Course\n\n![System-F](https://logo.systemf.com.au/systemf-450x450.png)\n\n### Written by Tony Morris \u0026 Mark Hibberd\n\n### With contributions from individuals and organisations (thanks!)\n\n#### Special note 1\n\nIf you have arrived here by https://github.com/system-f/fp-course and you are\nlooking for the *answers* (not the exercises), please go to https://github.com/tonymorris/fp-course\n\n#### Special note 2\n\nAs of February 2017, this repository is taking the place of the repository hosted at\n[https://github.com/NICTA/course](https://github.com/NICTA/course) which is deprecated.\n\nThe new repository is located at [https://github.com/system-f/fp-course](https://github.com/system-f/fp-course).\n\n#### Introduction\n\nThe course is structured according to a linear progression and uses the Haskell\nprogramming language to learn programming concepts pertaining to functional\nprogramming.\n\nExercises are annotated with a comment containing the word \"Exercise.\" The existing code compiles, however answers have\nbeen replaced with a call to the Haskell `error` function and so the code will throw an exception if it is run. Some\nexercises contain tips, which are annotated with a preceding \"Tip:\". It is not necessary to adhere to tips. Tips are\nprovided for potential guidance, which may be discarded if you prefer a different path to a solution.\n\nThe exercises are designed in a way that requires personal guidance, so if you\nattempt it on your own and feel a little lost, this is normal. All the\ninstructions are not contained herein.\n\n### Getting Help\n\nThere are two mailing lists for asking questions. All questions are welcome,\nhowever, your first post might be moderated. This is simply to prevent spam.\n\n1. [[nicta-fp]](https://groups.google.com/forum/#!forum/nicta-fp) is a Google\n   Group for any queries related to functional programming. This mailing list is\n   owned by System F and is open to the public. Questions relating to this course\n   are most welcome here.\n\n2. [[haskell-exercises]](https://groups.google.com/forum/#!forum/haskell-exercises)\n   is a Google Group for queries related specifically to this System F functional\n   programming course material. This mailing list is not owned by System F, but is\n   run by others who are keen to share ideas relating to the course.\n\n4. \\#bfpg [on Libera.chat](ircs://irc.libera.chat:6697/#bfpg) is the IRC channel of the\n   Queensland Functional Programming Lab - the team that runs the course in Brisbane.\n\n5. \\#scalaz [on Libera.chat](ircs://irc.libera.chat:6697/#scalaz) is an IRC channel that is operated\n   by others who are keen to share ideas relating to functional programming in\n   general. Most of the participants of this channel have completed the System F\n   functional programming course to some extent. They are in various timezones\n   and share a passion for functional programming, so may be able to provide\n   relatively quick assistance with questions.\n\n### Getting Started\n\n1. Install the Glasgow Haskell Compiler (GHC) version 8.0 or higher.\n   [GHCup](https://www.haskell.org/ghcup/) is the recommended way to do that these days\n\n2. Change to the directory containing this document.\n\n3. Execute the command `ghci`, which will compile and load all the source code.\n   You may need to set permissions on the root directory and the ghci configuration\n   file, `chmod go-w .ghci ./`.\n\n4. Inspect the introductory modules to get a feel for Haskell's syntax, then move\n   on to the exercises starting with `Course.Optional`. The\n   [Progression](#progression) section of this document lists the recommended\n   order in which to attempt the exercises.\n\n5. Edit a source file to a proposed solution to an exercise. At the `ghci`\n   prompt, issue the command `:reload`. This will compile your solution and\n   reload it in the GHC interpreter. You may use `:r` for short.\n\n### Tips after having started\n\n1. Some questions take a particular form. These are called *WTF questions*. WTF\n   questions are those of this form or similar:\n  * What does ____ mean?\n  * What does the ____ function mean?\n  * What is a ____ ?\n  * Where did ____ come from ?\n  * What is the structure of ____ ?\n\n  They are all answerable with the `:info` command. For example, suppose you\n  have the question, \"What does the `swiggletwoop` function mean?\" You may\n  answer this at GHCi with:\n\n  `\u003e :info swiggletwoop`\n\n  You may also use `:i` for short.\n\n2. Functional Programming techniques rely heavily on types. This reliance may\n   feel foreign at first, however, it is an important part of this course. If\n   you wish to know the type of an expression or value, use `:type`. For\n   example,\n\n   `\u003e :type reverse`\n\n   `List t -\u003e List t`\n\n   This tells you that the `reverse` function takes a list of elements of some\n   arbitrary type (`t`) and returns a list of elements of that same type. Try\n   it.\n\n   You may also use `:t` for short.\n\n3. GHCi has TAB-completion. For example you might type the following:\n\n   `\u003e :type rev`\n\n   Now hit the TAB key. If there is only one function in scope that begins with\n   the characters `rev`, then that name will auto-complete. Try it. This\n   completion is context-sensitive. For example, it doesn't make sense to ask\n   for the type of a data type itself, so data type names will not auto-complete\n   in that context, however, if you ask for `:info`, then they are included in\n   that context. Be aware of this when you use auto-complete.\n\n   This also works for file names:\n\n   `\u003e readFile \"/etc/pas\"`\n\n   Now hit the TAB key. If there is only one existing filename on a path that\n   begins with `/etc/pas`, then that name will auto-complete. Try it.\n\n   If there is more than one identifier that can complete, hit TAB twice\n   quickly. This will present you with your options to complete.\n\n4. Follow the types.\n\n   You may find yourself in a position of being unsure how to proceed for a\n   given exercise. You are encouraged to adopt a different perspective. Instead\n   of asking how to proceed, ask how you might proceed while adhering to the\n   guideline provided by the types for the exercise at hand.\n\n   It is possible to follow the types without achieving the desired goal,\n   however, this is reasonably unlikely at the start. As you become more reliant\n   on following the types, you will develop more trust in the potential paths\n   that they can take you, including identification of false paths.\n\n   Where types fall short, use the tests written in comments above each exercise.\n   They can be copied and pasted into GHCi. You should also take the first step\n   of following the types. Do it.\n\n5. Do not use tab characters\n\n   Set up your text editor to use space characters rather than tabs.\n   Using tab characters in Haskell can lead to confusing error messages.\n   GHC will give you a warning if your program contains a tab character.\n\n### Running the tests\n\nTests are stored under the `src/Test/` directory. Each module from the course that\nhas tests has a corresponding `\u003cMODULE\u003eTest.hs` file. Within each test module,\ntests for each function are grouped using the `testGroup` function. Within each\ntest group there are test cases (`testCase` function), and properties\n(`testProperty` function).\n\nTests are run using a built-in test runner that has no requirements\nbeyond those of the course (a supported version of GHCi). By default,\nthe full test suite is loaded, and each module's tests are\nexported. You can run the tests in GHCi like this:\n\n    \u003e\u003e test test_List\n\n#### Specific modules\n\nFor convenience, each test module also exports individual tests. To run tests\nfrom a single module, load it, and then run `test \u003ctests\u003e`. For example, in\n`GHCi`:\n\n    \u003e\u003e :l src/Test/ListTest.hs\n    \u003e\u003e test headOrTest\n    \u003e\u003e test productTest\n\n#### `:reload` and run tests\n\nThere is also a custom `:test` command defined in `.ghci` that will\ninvoke `:reload` and then `test` in a single action:\n\n    \u003e\u003e :test test_List\n    \u003e\u003e :test headOrTest\n\n#### doctest\n\nThe doctest tests are a mirror of the tests that reside in comments alongside\nthe code. They are not executable, but examples can be copied into GHCI.\nExamples begin with `\u003e\u003e\u003e` while properties begin with `prop\u003e`.\n\n### Progression\n\nWe recommend you perform some exercises before others. The first step\nis to inspect the introduction modules.\n\n* `Course.ExactlyOne`\n* `Course.Validation`\n\nThey contain examples of data structures and Haskell syntax. They do not contain\nexercises and exist to provide a cursory examination of Haskell syntax. The next\nstep is to complete the exercises in `Course.Optional`.\n\nAfter this, we recommend the following progression of modules:\n\n* `Course.List`\n* `Course.Functor`\n* `Course.Applicative`\n* `Course.Monad`\n* `Course.FileIO`\n* `Course.State`\n* `Course.StateT`\n* `Course.Extend`\n* `Course.Comonad`\n* `Course.Contravariant`\n* `Course.Compose`\n* `Course.Traversable`\n* `Course.ListZipper`\n* `Course.Parser` *(see also `Course.Person` for the parsing rules)*\n* `Course.MoreParser`\n* `Course.JsonParser`\n* `Course.Interactive`\n* `Course.Anagrams`\n* `Course.FastAnagrams`\n* `Course.Cheque`\n\nDuring this progression, it is often the case that some exercises are abandoned\ndue to time constraints and the benefit of completing some exercises over\nothers. For example, in the progression, `Course.Functor` to `Course.Monad`, the\nexercises repeat a similar theme. Instead, a participant may wish to do\ndifferent exercises, such as `Course.Parser`. In this case, the remaining\nanswers are filled out, so that progress on to `Course.Parser` can begin\n(which depends on correct answers up to `Course.Monad`). It is recommended to\ntake this deviation if it is felt that there is more reward in doing so.\n\nAnswers for the exercises can be found here:\n[https://github.com/tonymorris/fp-course](https://github.com/tonymorris/fp-course)\n\nAfter these are completed, complete the exercises in the `projects` directory.\n\n### Introducing Haskell\n\nThis section is a guide for the instructor to introduce Haskell syntax. Each of\nthese points should be covered before attempting the exercises.\n\n* values, assignment\n* type signatures `::` reads as *has the type*\n  * The `-\u003e` in a type signature is *right-associative*\n* functions are values\n* functions take arguments\n  * functions take *only one argument* but we approximate with spoken\n    language\n  * functions can be declared inline using *lambda expressions*\n  * the `\\` symbol in a lambda expression denotes a Greek lambda\n* operators, beginning with non-alpha character, are in infix position by\n  default\n  * use in prefix position by surrounding with *(parentheses)*\n* regular identifiers, beginning with alpha character, are in prefix position by\n  default\n  * use in infix position by surrounding with ``backticks``\n* polymorphism\n  * type variables *always* start with a lower-case character\n* data types, declared using the `data` keyword\n  * following the `data` keyword is the *data type name*\n  * following the data type name are zero of more type variables\n  * then `=` sign\n  * data types have zero or more constructors\n    * data type constructors start with an upper-case character, or colon `(:)`\n  * following each constructor is a list of zero or more *constructor arguments*\n  * between each constructor is a pipe symbol `(|)`\n  * the `deriving` keyword gives us default implementations for some functions\n    on that data type\n  * when constructors appear on the left side of `=` we are *pattern-matching*\n  * when constructors appear on the right side of `=` we are *constructing*\n* type-classes\n\n### Learning the tools\n\nWhen this course is run in-person, some tools, particularly within Haskell, are\ncovered first.\n\n* GHCi\n  * `:type`\n  * `:info`\n* values\n* type signatures\n  * `x :: T` is read as *x is of the type T*\n* functions are values\n* functions take arguments\n* functions take one argument\n* lambda expressions\n* operators (infix/prefix)\n  * identifiers starting with `isAlpha` are prefix by default, infix surrounded in backticks (\\`)\n  * other identifiers are infix by default, prefix surrounded in parentheses\n* data types\n  * `data` keyword\n  * recursive data types\n* pattern matching\n* `deriving` keyword\n* type-classes\n* type parameters\n  * always lower-case 'a'..'z'\n  * aka generics, templates C++, parametric polymorphism\n\n### Parser grammar assistance\n\nThe exercises in `Parser.hs` can be assisted by stating problems in a specific way, with a conversion to code.\n\n| English   | Parser library                    |\n|-----------|-----------------------------------|\n| and then  | `bindParser` `\u003e\u003e=`                |\n| always    | `valueParser` `pure`              |\n| or        | `\\|\\|\\|`                             |\n| 0 or many | `list`                            |\n| 1 or many | `list1`                           |\n| is        | `is`                              |\n| exactly n | `thisMany n`                      |\n| call it x | `\\x -\u003e`                           |\n\n### Monad comprehension\n\n##### do-notation\n\n* insert the word `do`\n* turn `\u003e\u003e=` into `\u003c-`\n* delete `-\u003e`\n* delete `\\`\n* swap each side of `\u003c-`\n\n##### LINQ\n\n* write `from` on each line\n* turn `\u003e\u003e=` into in\n* delete `-\u003e`\n* delete `\\`\n* swap each side of `in`\n* turn value into `select`\n\n### Demonstrate IO maintains referential transparency\n\nAre these two Haskell programs, the same program?\n\n    p1 ::\n      IO ()\n    p1 =\n      let file = \"/tmp/file\"\n      in  do  _ \u003c- writeFile file \"abcdef\"\n              x \u003c- readFile file\n              _ \u003c- putStrLn x\n              _ \u003c- writeFile file \"ghijkl\"\n              y \u003c- readFile file\n              putStrLn (show (x, y))\n\n    p2 ::\n      IO ()\n    p2 =\n      let file = \"/tmp/file\"\n          expr = readFile file\n      in  do  _ \u003c- writeFile file \"abcdef\"\n              x \u003c- expr\n              _ \u003c- putStrLn x\n              _ \u003c- writeFile file \"ghijkl\"\n              y \u003c- expr\n              putStrLn (show (x, y))\n\nWhat about these two Python programs?\n\n    def writeFile(filename, contents):\n        with open(filename, \"w\") as f:\n            f.write(contents)\n\n    def readFile(filename):\n        contents = \"\"\n        with open(filename, \"r\") as f:\n            contents = f.read()\n            return contents\n\n    def p1():\n        file = \"/tmp/file\"\n\n        writeFile(file, \"abcdef\")\n        x = readFile(file)\n        print(x)\n        writeFile(file, \"ghijkl\")\n        y = readFile(file)\n        print (x + y)\n\n    def p2():\n        file = \"/tmp/file\"\n        expr = readFile(file)\n\n        writeFile(file, \"abcdef\")\n        x = expr\n        print(x)\n        writeFile(file, \"ghijkl\")\n        y = expr\n        print (x + y)\n\nWhat about these two Clojure programs?\n\n    (use 'clojure.java.io)\n\n    (defn p1 []\n        (let\n            [ file \"/tmp/file\" ]\n        (do\n            (spit file \"abcdef\")\n            (let\n                [ x (slurp file) ]\n            (do\n                (println x)\n                (spit file \"ghijkl\")\n                (let [y (slurp file)]\n                (println (str x y)))\n            ))\n        ))\n    )\n\n    (defn p2 []\n        (let\n            [ file \"/tmp/file\"\n              expr (slurp file)\n            ]\n        (do\n            (spit file \"abcdef\")\n            (let [x expr]\n            (do\n                (println x)\n                (spit file \"ghijkl\")\n                (let [y expr]\n                (println (str x y)))\n            ))\n        ))\n    )\n\n### One-day\n\nSometimes this course material is condensed into one-day. In these cases, the\nfollowing exercises are recommended:\n\n* `Optional`\n  * `mapOptional`\n  * `bindOptional`\n  * `(??)`\n  * `(\u003c+\u003e)`\n* `List`\n  * `headOr`\n  * `product`\n  * `length`\n  * `map`\n  * `filter`\n  * `(++)`\n  * `flatMap`\n  * `reverse`\n* `Functor`\n  * `instance Functor List`\n  * `instance Functor Optional`\n  * `instance Functor ((-\u003e) t)`\n  * `instance Functor void`\n* `Applicative`\n  * `instance Applicative List`\n  * `instance Applicative Optional`\n  * `instance Applicative ((-\u003e) t)`\n  * `lift2`\n  * `sequence`\n* `FileIO`\n\n### What about cabal and stack?\n\nThis repository's primary purpose is to support in-person instruction\nfor people who have potentially not even used development tools at\nall. We have therefore designed the course around `ghci` as the\nprimary tool.\n\nIf you are a more experienced developer with tooling set up, and you\nneed a cabal file, `shell.nix` or `stack.yaml` to have working\ndevelopment tools, run the `support/copy-tool-files.sh` script from\nthe root of the repository.\n\n(Windows users, try running `support\\copy-tool-files.bat` from the\nrepository root.)\n\n### References\n\n* [The Haskell `error` function](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:error)\n\n* [Glasgow Haskell Compiler](http://haskell.org/ghc)\n","funding_links":[],"categories":["Paradigm","Haskell","Projects"],"sub_categories":["Courses, etc."],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystem-f%2Ffp-course","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsystem-f%2Ffp-course","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystem-f%2Ffp-course/lists"}