├── chapter-five-error-handling.md ├── .gitbook └── assets │ ├── 03_haskell_lizard.png │ ├── 02_haskell_wizard-1080h.png │ ├── 05_haskell_high_priestess.png │ └── unlimitedpower-funny-gifs.gif ├── chapter-five-web-api-dsl.md ├── appendix ├── faq.md ├── haskell-wizards.md ├── optics.md └── fission-style-guide.md ├── SUMMARY.md ├── chapter-four-database.md ├── README.md ├── chapter-one-setup.md ├── chapter-four-lifting.md ├── chapter-two-global-config.md ├── chapter-two-type-classes.md ├── part-i.md └── LICENSE /chapter-five-error-handling.md: -------------------------------------------------------------------------------- 1 | # Chapter Five: Error Handling 2 | 3 | -------------------------------------------------------------------------------- /.gitbook/assets/03_haskell_lizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fission-codes/haskell-for-typescript-devs/HEAD/.gitbook/assets/03_haskell_lizard.png -------------------------------------------------------------------------------- /.gitbook/assets/02_haskell_wizard-1080h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fission-codes/haskell-for-typescript-devs/HEAD/.gitbook/assets/02_haskell_wizard-1080h.png -------------------------------------------------------------------------------- /.gitbook/assets/05_haskell_high_priestess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fission-codes/haskell-for-typescript-devs/HEAD/.gitbook/assets/05_haskell_high_priestess.png -------------------------------------------------------------------------------- /.gitbook/assets/unlimitedpower-funny-gifs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fission-codes/haskell-for-typescript-devs/HEAD/.gitbook/assets/unlimitedpower-funny-gifs.gif -------------------------------------------------------------------------------- /chapter-five-web-api-dsl.md: -------------------------------------------------------------------------------- 1 | # Chapter Seven: Web API 2 | 3 | ## Intro 4 | 5 | ## API-as-Type 6 | 7 | ## Autogenerated Clients 8 | 9 | ## Composable Servers 10 | 11 | ### RIOServer 12 | 13 | -------------------------------------------------------------------------------- /appendix/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Haskell Tips & Tricks 3 | --- 4 | 5 | # Appendix I: FAQ 6 | 7 | ## How do I change my REPL prompt? 8 | 9 | Here's a simple one liner to set your prompt: 10 | 11 | `:set prompt "λ> "` 12 | 13 | To set it permanently, look at the config file in `~/.ghci`. Here's an example custom prompt: 14 | 15 | ```text 16 | :set prompt "\ESC[1;34m\n\ESC[0;34mλ> \ESC[m" 17 | -- :seti -XNoImplicitPrelude 18 | :seti -XOverloadedStrings 19 | :seti -XScopedTypeVariables 20 | 21 | :set -Wall 22 | :set -fno-warn-type-defaults 23 | :set -package pretty-show 24 | 25 | import Text.Show.Pretty (pPrint) 26 | :set -interactive-print pPrint 27 | :set +s 28 | :set +t 29 | 30 | :def rt const $ return $ unlines [":r", ":main --rerun-update"] 31 | :def rtfe const $ return $ unlines [":r", ":main --rerun-update --rerun-filter failures,exceptions"] 32 | :def rtn const $ return $ unlines [":r", ":main --rerun-update --rerun-filter new"] 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Haskell for TypeScript Developers](README.md) 4 | * [Syntactic Cheat Sheet](part-i.md) 5 | * [Chapter One: Setup](chapter-one-setup.md) 6 | * [Chapter Two: Type Classes](chapter-two-type-classes.md) 7 | * [Chapter Three: Ambient Config](chapter-two-global-config.md) 8 | * [Chapter Four: Lifting](chapter-four-lifting.md) 9 | * [Chapter Five: Error Handling](chapter-five-error-handling.md) 10 | * [Chapter Six: Database DSL](chapter-four-database.md) 11 | * [Chapter Seven: Web API](chapter-five-web-api-dsl.md) 12 | 13 | ## Appendix 14 | 15 | * [Appendix I: FAQ](appendix/faq.md) 16 | * [Appendix II: Optics](appendix/optics.md) 17 | * [Appendix III: Fission Style Guide](appendix/fission-style-guide.md) 18 | * [Haskell Wizards](appendix/haskell-wizards.md) 19 | * [Fission - Web native file system, app & web hosting](https://fission.codes) 20 | * [Fission Web API in Haskell](https://github.com/fission-suite/fission) 21 | * [IPFS on Hackage](https://hackage.haskell.org/package/ipfs) 22 | 23 | -------------------------------------------------------------------------------- /chapter-four-database.md: -------------------------------------------------------------------------------- 1 | # Chapter Six: Database DSL 2 | 3 | ## The Legend of Selda 4 | 5 | Haskell has many database libraries, and there's a recent trend in leveraging the type system to giving increasingly safe database access. Despite the goofy name, [Selda](https://selda.link/) is a very robust database DSL, and works equally well for PostgreSQL or SQLite. 6 | 7 | {% hint style="info" %} 8 | The website for Selda is literally `https://selda.link`⚔️🛡️🤣 9 | {% endhint %} 10 | 11 | {% hint style="info" %} 12 | We may later switch to [Beam](https://tathougies.github.io/beam/), which is more feature rich, has generally faster compilation, and has a clearer tutorial. We switched to Selda due to a bug in a dependency, which has since been fixed. 13 | {% endhint %} 14 | 15 | The flip side of having so much power in a database library is that the types can get pretty out of hand sometimes. This is a trade-off that we make in a number of places throughout the application, and is highly beneficial 98% of the time. Selda uses a lot of constraint aliases to merge multiple constraints into one. GHC or Intero will give you hints with the underlying constraints. We'll touch of a few things to keep in mind below. 16 | 17 | ## MonadSelda 18 | 19 | {% code title="Fission.Storage.Types" %} 20 | ```haskell 21 | type SeldaPool = Database.Pool (SeldaConnection SQLite) 22 | 23 | newtype Pool = Pool { getPool :: SeldaPool } 24 | deriving Show 25 | ``` 26 | {% endcode %} 27 | 28 | {% code title="Fission.Internal.Orphanage" %} 29 | ```haskell 30 | instance Has DB.Pool cfg => MonadSelda (RIO cfg) where 31 | type Backend (RIO cfg) = SQLite 32 | 33 | withConnection action = do 34 | DB.Pool pool <- Config.get 35 | withResource pool action 36 | ``` 37 | {% endcode %} 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | A guide to bootstrapping your TypeScript experience to become an effective 4 | Haskeller 5 | --- 6 | 7 | # Haskell for TypeScript Developers 8 | 9 | ## Foreword 10 | 11 | I'm Brooklyn Zelenka, the author of this guide and CTO and co-founder at [Fission](https://fission.codes). You can find many of my presentations and keynotes on [Notist](https://noti.st/expede), and on most online spaces as **@expede**, including [Twitter](https://twitter.com/expede) and [Github](https://github.com/expede). 12 | 13 | > Avoid "success at all costs" 14 | > 15 | > _— Motto of the Haskell Core Dev Team_ 16 | 17 | Haskell was explicitly designed to be an _influential_ language; to get concepts used by other languages. To this end it has been extraordinarily successful. The JavaScript ecosystem has increasingly to borrowed concepts, patterns, and features from the ML-family of languages over the past few years. 18 | 19 | As someone who is fairly associated with functional programming, and having taught a number of functional languages and techniques to many teams over the past several years, I've been increasingly called on to teach _specifically Haskell_ to curious developers. If you're looking for an excellent introductory book with practice problems and the like, I recommend [Haskell from First Principles](http://haskellbook.com/). However, not every team has the time to go through a 1000+ page book, and need to get up and running in production code quickly by leveraging existing experience with other languages. 20 | 21 | {% hint style="info" %} 22 | In this guide, you will get a working understanding of how to work in a 2019 industry-style web project by drawing on your existing experience with TypeScript 23 | {% endhint %} 24 | 25 | {% hint style="warning" %} 26 | This guide is designed to onboard developers at [Fission](https://fission.codes). We are opinionated about how we do Haskell, and this guide is not meant as a guide to "all things Haskell" 27 | {% endhint %} 28 | 29 | -------------------------------------------------------------------------------- /appendix/haskell-wizards.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Character illustrations of Haskell users 3 | --- 4 | 5 | # Haskell Wizards 6 | 7 | Our main [web service API at Fission](https://github.com/fission-suite/fission) is written in Haskell. Through a series of internal company jokes, we ended up coming up with the concept of Haskell wizards and lizards. Sometimes you feel like a wizard and can tackle anything, some times you feel more lizard-y and need to take some time to think and design before you're feeling confident again. 8 | 9 | We ended up adding the High Priestess as a third character who is a more femme expression of the concepts we were going for. 10 | 11 | #### Licensing & Usage 12 | 13 | You are welcome to use these illustrations. They are licensed under [Creative Commons By Attribution Share Alike \(CC BY-SA\) » ](https://creativecommons.org/licenses/by-sa/4.0/) 14 | 15 | Please link to [fission.codes](https://fission.codes) for attribution. If you need the original source files for remixing, please get in touch. 16 | 17 | Artwork created by [Bruno Monts](https://www.behance.net/brunomonts), a Brazillian illustrator. 18 | 19 | ### Haskell Wizard 20 | 21 | ![](../.gitbook/assets/02_haskell_wizard-1080h.png) 22 | 23 | The Haskell Wizard is part magic, part science. They're confident and proficient, using both tech tools and the magic of type theory, a scientist's lab coat, and a wizard hat, to tackle any software design challenge. 24 | 25 | ### Haskell Lizard 26 | 27 | ![](../.gitbook/assets/03_haskell_lizard.png) 28 | 29 | The Haskell Lizard is enjoying their Haskell journey, but sometimes feels a little confused. Is the compiler complaining AGAIN? Maybe it's just that another cup of coffee is needed, or an extra read through some Haskell books. 30 | 31 | ### Haskell High Priestess 32 | 33 | ![](../.gitbook/assets/05_haskell_high_priestess.png) 34 | 35 | The Haskell high priestess has got your back. She is stern but fair, wants you to get better, and is there to teach and support you. 36 | 37 | The Haskell High Priestess currently graces the [Fission Support Page »](https://fission.codes/support) 38 | 39 | -------------------------------------------------------------------------------- /chapter-one-setup.md: -------------------------------------------------------------------------------- 1 | # Chapter One: Setup 2 | 3 | ## Build Tool 4 | 5 | We use [Stack](https://docs.haskellstack.org/en/stable/README/) as our build tool, and [HPack](https://github.com/sol/hpack) for package configuration. Dependencies, test modules, executables to build, compiler flags, and the like are found in `packag.yaml`. 6 | 7 | ## Intero 8 | 9 | If you install [Intero](https://haskell-lang.org/intero), you'll get a bunch of nice editor/IDE features out of the box [🚀](https://emojipedia.org/rocket/) 10 | 11 | ## Language Extensions 12 | 13 | GHC \(the main Haskell compiler\) has a number of "language extensions" available. These are things that are available in GHC, but not yet in the language spec \(which is updated every 10 years or so\). These are primarily to modernize to the lanaguge, and make your life much easier. For reference, here's the list of extension that we have enabled standard. 14 | 15 | `ApplicativeDo`, `BangPatterns`, `BlockArguments`, `ConstraintKinds`, `DataKinds`, `DeriveAnyClass`, `DeriveFoldable`, `DeriveFunctor`, `DeriveGeneric`, `DeriveLift`, `DeriveTraversable`, `DerivingStrategies`, `FlexibleContexts`, `FlexibleInstances`, `FunctionalDependencies`, `GADTs`, `GeneralizedNewtypeDeriving`, `KindSignatures`, `LambdaCase`, `LiberalTypeSynonyms`, `MultiParamTypeClasses`, `MultiWayIf`, `NamedFieldPuns`, `NoImplicitPrelude`, `NoMonomorphismRestriction`, `OverloadedStrings`, `OverloadedLabels`, `OverloadedLists`, `RankNTypes`, `RecordWildCards`, `ScopedTypeVariables`, `StandaloneDeriving`, `TupleSections`, `TypeSynonymInstances`, `TemplateHaskell`, `TypeOperators`, `ViewPatterns` 16 | 17 | Wow, that looks like a long list! Most of these enable quality-of-life improvements with new syntax or remove boilerplate. Almost all of these are used widely in industry, and are considered totally safe to use \(and in some cases, it's masochistic to _not_ have them enabled, like `OverloadedStrings`\). You can read up more on these at the extremely informative [What I Wish I Knew When Learning Haskell](http://dev.stephendiehl.com/hask/). 18 | 19 | We mention this here becasue you may find reference to them while on Stack Overflow, or some syntax that's not available in older references like the now woefully out-of-date Learn You a Haskell for Great Good \(which I'm not even going to link\). 20 | 21 | ## Project Structure 22 | 23 | A project is broken into a reusable library, executables, tests, and benchmarks. 24 | 25 | ### Library 26 | 27 | You will often see library code in a `/src` directory. We're following the other convention, which is to place library code in a directory shocking titled... `library`. 28 | 29 | ### Quality 30 | 31 | Quaity checks live in `/test`. We have several kinds of tests: 32 | 33 | | Type | Directory | 34 | | :--- | :--- | 35 | | Unit Tests | `/test/testsuite` | 36 | | Doctests | Setup in `/test/doctest`; actual tests in each source file in `/library` | 37 | | Code Linter | `/test/lint` | 38 | | Code Coverage | `/test/coverage-code` | 39 | | Doc Coverage | `/test/coverage-docs` | 40 | 41 | ### Benchmarks 42 | 43 | Benchmarks live at `/bench` 44 | 45 | ### Executables 46 | 47 | If a project contains a single executable, it tends to live in `/app` or `/exe`. Since our project contains multiple executables, we use the name of the executable itself \(for example `/fission-web`\). 48 | 49 | ## Where to Find Packages 50 | 51 | ### Stackage 52 | 53 | We use [Stackage](https://www.stackage.org/) for nearly all packages. These are stable packages, grouped into "Stack resolver versions", so you don't need to worry about version numbers. Just stick them in your dependencies, and everything just works™. 54 | 55 | {% code title="package.yaml" %} 56 | ```yaml 57 | dependencies: 58 | - aeson 59 | - aeson-casing 60 | - base 61 | - [...] 62 | ``` 63 | {% endcode %} 64 | 65 | Searching in Stackage can be done by name, _or by type signature_, which is very useful. 66 | 67 | ### Hackage 68 | 69 | You can find even more packages on [Hackage](http://hackage.haskell.org/). It's a very similar process as with Stackage, but to include packages 70 | 71 | {% code title="stack.yaml" %} 72 | ```yaml 73 | extra-deps: 74 | - alex-3.2.4 75 | - ekg-wai-0.1.0.3 76 | - happy-1.19.10 77 | - servant-multipart-0.11.4 78 | - tasty-rerun-1.1.14 79 | ``` 80 | {% endcode %} 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /chapter-four-lifting.md: -------------------------------------------------------------------------------- 1 | # Chapter Four: Lifting 2 | 3 | Something that we're going to sidestep almost entirely is a concept called [monad transformers](https://en.wikibooks.org/wiki/Haskell/Monad_transformers). We rarely use them _directly_ in practice, even though they're all through our dependencies, or isolated to type class instances or more semantic helper functions. Remember that the purpose of this guide is to give a pragmatic up-and-running style introduction to being productive in Haskell. No one asks how the plumbing _inside_ TypeScript promises work, but it's easy to follow along with the high-level syntax and concepts. 4 | 5 | ## Levels 6 | 7 | Your application will sometimes consist of several nested data types. For example: 8 | 9 | ```haskell 10 | Either Error (Maybe [a]) 11 | ``` 12 | 13 | You probably actually care about the `a` values, but need to work with type class instances for `Either`, `Maybe`, and `List`. We can think of these as "levels" of wrappers: 14 | 15 | ```text 16 | +--------Either--------+ 17 | | +--Maybe--+ | 18 | | | | | 19 | | Error | [ a ] | | 20 | | | | | 21 | | +---------+ | 22 | +----------------------+ 23 | ``` 24 | 25 | ### Mixing Levels 26 | 27 | Clearly there's a lot of layers that you can think about. If all you care about is the outermost `Either`, then it's pretty straightforward. To access the inner `Maybe`, you need to find a way to get into `Either` structure, do what you need at that level, and get back out again. You can have all of your functions wrap and unwrap the various layers, but that composes badly and leads to a lot of boilerplate. 28 | 29 | The solution is to `lift` a function through the layers. Take something that works on one layer, and make it work on another. We can do this manually, but for all of the common data types, there are type classes to help us do this in a straightforward way. By far the most common one is `liftIO`. 30 | 31 | {% hint style="info" %} 32 | Everything in a Haskell application occurs inside the `IO` type, which is where the horrible, unpredictable, tainted imperative world touches your beautiful, pristine functional clockwork. 33 | 34 | Haskell doesn't expose the `IO` constructor \(i.e. it's not exported, something you can do in your modules, too\), so we want to avoid touching `IO` directly as much as possible because it's impossible to get fully out of it. At the end of the day your function will get used in a `IO` context \(even if you're several layers down\). Most of the Haskell style is creating clean, pure, high-level DSLs, and calling _those_ inside `IO`. 35 | {% endhint %} 36 | 37 | ## MonadIO 38 | 39 | `IO` actions occur frequently. Accessing CLI input, talking to the database, and making a network request are all things that you'll want to do on a regular basis. If the monad that you're working in also has a `MonadIO` instance, you get access to `liftIO`. Fission uses `MonadRIO` very frequently, which inherits from `MonadIO`. The best way to read `liftIO` is as "turn an `IO` function into one that happens in my current wrapper." 40 | 41 | {% hint style="info" %} 42 | This example uses an `IO`-enabled wrapper: `m`. It's done this way to make it match easily with any other `MonadIO` instance, rather than using IO directly. 43 | {% endhint %} 44 | 45 | {% code title="Fission.Timestamp" %} 46 | ```haskell 47 | addM :: MonadIO m => Unstamped r -> m r 48 | addM record = do 49 | now <- liftIO getCur rentTime 50 | return $ add now record 51 | ``` 52 | {% endcode %} 53 | 54 | ### Specifically RIO 55 | 56 | Let's look at something that uses a concrete wrapping type, in this case `RIO`. 57 | 58 | {% hint style="info" %} 59 | This example uses `RIO` directly because it's used while setting up the database pool for our ambient config on app startup, and it's more convenient phrased this way for this scenario. 60 | {% endhint %} 61 | 62 | {% code title="Fission.Storage.SQLite" %} 63 | ```haskell 64 | connPool :: HasLogFunc cfg => DB.Path -> RIO cfg DB.Pool 65 | connPool (DB.Path {getPath = path}) = do 66 | logDebug $ "Establishing DB pool for " <> displayShow path 67 | 68 | rawPool <- liftIO $ createPool (sqliteOpen path) seldaClose 4 2 10 69 | logDebug $ "DB pool stats: " <> displayShow rawPool 70 | 71 | return $ DB.Pool rawPool 72 | ``` 73 | {% endcode %} 74 | 75 | The function `createPool :: IO (Pool SeldaConnection)` needs to be brought into `RIO`. Remember that `IO` doesn't export a constructor for us to explicitly destructure and repackage! We need a way to turn `IO a` into `RIO cfg a`. 76 | 77 | {% hint style="success" %} 78 | `RIO` stands for "`Reader` + `IO`." Clearly this has a `MonadIO` instance! 79 | {% endhint %} 80 | 81 | We can always write this function manually, but by far the easiest is the `liftIO` instance for `RIO`. The upshot is that it makes `IO` actions compatible with the rest of your `RIO` function. 82 | 83 | -------------------------------------------------------------------------------- /appendix/optics.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Common Lens Legend 3 | --- 4 | 5 | # Appendix II: Optics 6 | 7 | My guess is that you're seeing lenses and going "what are all these funky looking operators? 😱" In short, they're getters and setters that let you work on nested data, enums, `Maybe`s, and so on. They compose nicely, so you can all kinds of mutable-feeling updates in a totally controlled way. 8 | 9 | The good news is that once you know the lens library, it's used in a TON of places, so it's not wasted effort. You can really get by with knowing a handful of them, and following the patterns. 10 | 11 | The idea is that you're zooming into a data structure. You can grab values out, or edit them. Then you zoom back out and the outer structures have also been updated to reflect point at the inner changes. 12 | 13 | #### Getters `^` 14 | 15 | * `.` go deeper / compose \(normal composition operator\) 16 | * `^.` get / lookup 17 | * `^?` get nullable field \(see `Nullable` below\) 18 | 19 | #### Setters `~` 20 | 21 | * `&` mutate with / and also \(it's the normal pipe / reverse application operator\) 22 | * `.~` set / replace 23 | * `?~` set a nullable field \(see `Nullable` below\) 24 | * `%~` update with a function \(run function on current value and set it to that\) 25 | * `+~` add to the current value \(e.g. counter\) 26 | * `-~` subtract from current value 27 | * `*~` multiple current value by 28 | 29 | #### Nullable `?` 30 | 31 | * `^?` get inside a `Maybe` \(i.e. stop lensing if it's a `Nothing`\) 32 | * `?~` set a nullable field \(i.e. `.~ Just newValue`\) 33 | 34 | These read nicely, and let you make deeply nested updates either with composition \(`.`\) or with a named lens that is a composition of others. 35 | 36 | ### Why not use record-update syntax? 37 | 38 | You totally can! Doing this manually, you end up having to update each nested record recursively, including the updated sub-records as you go. For example: 39 | 40 | ```haskell 41 | data Person = Person 42 | { _name :: Text 43 | , _address :: Address 44 | , _pet :: Pet 45 | } 46 | 47 | data Pet = Pet 48 | { _name :: Text 49 | , _variety :: Animal 50 | , _favouriteToy :: Toy 51 | } 52 | 53 | data Animal = Dog | Cat | Fish 54 | 55 | data Toy = Toy 56 | { _ name :: Text 57 | , _condition :: Condition 58 | } 59 | 60 | data Condition = New | Good | Fair | Bad 61 | ``` 62 | 63 | Let's define an `owner :: Person`: 64 | 65 | ```haskell 66 | owner = Person 67 | { _name = "Alice" 68 | , _address = "123 Fake Street" 69 | , _pet = Pet 70 | { _name = "Fluffy" 71 | , _variety = Cat 72 | , _favouriteToy = Toy 73 | { _name = "wind-up mouse" 74 | , _condition = New 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | Manually updating deeply nested records is a bit ugly 81 | 82 | ```haskell 83 | wearAndTear :: Person 84 | wearAndTear = owner { _pet = myPet { _favouriteToy = myToy { _condition = Fair } } } 85 | where 86 | Person { _pet = myPet@(Pet { _favouriteToy = myToy })} = owner 87 | ``` 88 | 89 | Lenses read better 90 | 91 | ```haskell 92 | wearAndTear :: Person 93 | wearAndTear = owner & pet . favouriteToy . condition .~ Fair 94 | ``` 95 | 96 | Let's break that down 🕺 97 | 98 | ```haskell 99 | -- Pipe Replace 100 | -- | | 101 | -- v v 102 | wearAndTear = owner & pet . favouriteToy . condition .~ Fair 103 | -- ^ ^ ^ ^ ^ 104 | -- | | | | | 105 | -- | +---------+-----------+ Replace with 106 | -- Initial Value | 107 | -- Nested path 108 | -- (like in OO dot-notation) 109 | -- owner.pet.favouriteToy.condition 110 | ``` 111 | 112 | These can also be turned into helper functions, chained together, and so on 113 | 114 | ```haskell 115 | playWithPet :: Person -> Person 116 | playWithPet = pet . favouriteToy . condition .~ Fair 117 | 118 | -- Use 119 | 120 | playWithPet owner 121 | 122 | -- Oh no! Fluffy ran away 😭 Let's play with our new cat: Mittens! 123 | 124 | owner & pet.name.~"Mittens" 125 | & playWithPet 126 | 127 | -- And exciting! We moved, and got a dog to play with 128 | 129 | owner & address .~ "1066 West Hastings Street" 130 | & pet.variety .~ Dog 131 | & pet.name .~ "Lassie" 132 | & pet.toy.name .~ "Tennis ball" 133 | & playWithPet 134 | ``` 135 | 136 | We can also automate some behaviour while we're at it: 137 | 138 | ```haskell 139 | playWithPet' :: Person -> Person 140 | playWithPet' = pet . favouriteToy . condition %~ wearDown 141 | 142 | wearDown :: Condition -> Condition 143 | wearDown New = Good 144 | wearDown Good = Fair 145 | wearDown Fair = Bad 146 | wearDown Bad = Bad 147 | ``` 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /chapter-two-global-config.md: -------------------------------------------------------------------------------- 1 | # Chapter Three: Ambient Config 2 | 3 | ## Why Ambient? 4 | 5 | Often things like configuration variables read in from the command line or a file, shared database pools, or functions that we want to use in our application at runtime but swapped out for development or at test-time \(i.e. [dependency injection](https://nehalist.io/dependency-injection-in-typescript/)\). 6 | 7 | Haskell is a "pure" language where everything is explicit. As a naive approach, this can sometimes lead to boilerplate function arguments for threading a value around your application. This also means many changes when something breaks. To get around this, we sometimes want to have something available "ambiently" in an application, as if it were a _global constant_, and have all that threading done for us by helper functions. 8 | 9 | This is a standard pattern known as a `Reader`. It's so common that the Haskell community has started rapidly embracing it as _the_ wrapper for applications. You can have global and local `Reader`s, but we'll be focusing here on the global one for our application. 10 | 11 | ## App Config 12 | 13 | The first part of this is to create a data structure that will contain our global context. Many people call this `Env`, but that can conflict with standard terminology for environment variables, which environment an application is running in \(test, development, staging, production\), and so on. We have opted to call this `Config`. 14 | 15 | {% code title="Fission.Config.Types" %} 16 | ```haskell 17 | data Config = Config 18 | { _logFunc :: !LogFunc 19 | , _host :: !Web.Host 20 | , _dbPath :: !DB.Path 21 | , _dbPool :: !DB.Pool 22 | -- and so on 23 | } 24 | 25 | makeLenses ''Config 26 | ``` 27 | {% endcode %} 28 | 29 | {% hint style="info" %} 30 | We may move to [SuperRecord](https://www.athiemann.net/2017/07/02/superrecord.html) in the future, for _even less_ boilerplate plus some nifty additional super powers [🦸](https://emojipedia.org/superhero/) 31 | {% endhint %} 32 | 33 | {% hint style="success" %} 34 | Keeping your Config as flat as possible is generally a good idea. While many people have an intuition that nesting things by concept \(e.g. database\) is a good idea, it's generally more trouble than it's worth in practice. 35 | {% endhint %} 36 | 37 | ## Explicit Constraints 38 | 39 | While we have all of this threading done for us, we still want to know which part of the config is required by which part of the application. This approach has a few upsides: 40 | 41 | 1. Easy to read labels help you keep track of what a function can access 42 | 2. Functions don't depend on specific concrete `Config`s, just fields 43 | 3. The compiler can help you refactor if change the `Config` 44 | 4. Constraints bubble up to callers, so dependencies can't hide 45 | 46 | Here's an example that retrieves the web host name, and combines it with a ncie message that is passed in as an argument: 47 | 48 | ```haskell 49 | hostMsg :: MonadRIO cfg m 50 | => Has Web.Host cfg 51 | => Text 52 | -> m Text 53 | hostMsg greeting = do 54 | Web.Host hostname <- Config.get 55 | return $ greeting <> ", the app is live at " <> hostname 56 | ``` 57 | 58 | `Config.get` pulls out a value from the `Config`. It knows which value you want because of the expected constructor \(`Web.Host`\) on the left side of the `<-`. 59 | 60 | {% hint style="danger" %} 61 | `MonadRIO` is a constraint defined in our application. It's an alias for the very common scenario in this style of wanting both `MonadIO m` and `MonadReader cfg m`. 62 | {% endhint %} 63 | 64 | ### Logging 65 | 66 | Our prelude \([`RIO`](https://www.fpcomplete.com/blog/2017/07/the-rio-monad)\) depends on having functions available ambiently in this way. One common case is with logging, which needs a `LogFunc` ready for use. 67 | 68 | ```haskell 69 | logHost :: MonadRIO cfg m 70 | => Has Web.Host cfg 71 | => HasLogFunc cfg 72 | -> m () 73 | logHost = logInfo $ "Host name is " <> display hostname 74 | ``` 75 | 76 | {% hint style="success" %} 77 | Unlike the first example, the constraint has no space after the `Has`. This is because we're using the [Has library](http://hackage.haskell.org/package/data-has) to clean up some of the boilerplate associated with creating so many constraints. 78 | {% endhint %} 79 | 80 | ## Adding a New Field to the Config 81 | 82 | You are likely to want to add custom fields to the `Config` record. The first step is to ensure that the type is unique to the application, wrapping common types in `newtype`: 83 | 84 | {% code title="Fission.Web.Types" %} 85 | ```haskell 86 | newtype Port = Port { getPort :: Int } 87 | deriving Show 88 | ``` 89 | {% endcode %} 90 | 91 | Next, add it to the `Config` itself: 92 | 93 | {% code title="Fission.Config.Types" %} 94 | ```haskell 95 | data Config = Config 96 | { _logFunc :: !LogFunc 97 | , _host :: !Web.Host 98 | , _port :: !Web.Port -- THIS LINE 99 | , _dbPath :: !DB.Path 100 | , _dbPool :: !DB.Pool 101 | } 102 | 103 | makeLenses ''Config 104 | ``` 105 | {% endcode %} 106 | 107 | {% hint style="success" %} 108 | Because of the `makeLenses` declaration, you automagically get a lens \(superpowered accessor\) for the new field called `port` 109 | 110 | `Unsure of what lenses are? check out our`[`Common Lens Legend`](appendix/optics.md)\`\` 111 | {% endhint %} 112 | 113 | Finally, write a [`Has` instance](https://www.stackage.org/haddock/lts-14.5/data-has-0.3.0.0/Data-Has.html): 114 | 115 | ```haskell 116 | instance Has Web.Port Config where 117 | hasLens = port 118 | ``` 119 | 120 | That's it! It's available everywhere in the application now! 121 | 122 | ```haskell 123 | logPort :: MonadRIO cfg m 124 | => Has Web.Port cfg 125 | => HasLogFunc cfg 126 | => m () 127 | logPort = do 128 | Web.Port port <- Config.get 129 | logInfo $ displayShow port 130 | ``` 131 | 132 | -------------------------------------------------------------------------------- /appendix/fission-style-guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: We focus on storytelling in our code 3 | --- 4 | 5 | # Appendix III: Fission Style Guide 6 | 7 | ## Type Signatures 8 | 9 | Type signatures are required 10 | 11 | ### Single-Line 12 | 13 | {% tabs %} 14 | {% tab title="Right" %} 15 | ```haskell 16 | id :: a -> a 17 | id x = x 18 | ``` 19 | {% endtab %} 20 | 21 | {% tab title="Wrong" %} 22 | ```haskell 23 | id 24 | :: a 25 | -> a 26 | id x = x 27 | ``` 28 | {% endtab %} 29 | {% endtabs %} 30 | 31 | ### Multi-Line 32 | 33 | {% tabs %} 34 | {% tab title="Right" %} 35 | ```haskell 36 | rawList :: 37 | ( MonadRIO cfg m 38 | , Has IPFS.BinPath cfg 39 | , Has IPFS.Timeout cfg 40 | , HasProcessContext cfg 41 | , HasLogFunc cfg 42 | ) 43 | => m (ExitCode, Lazy.ByteString, Lazy.ByteString) 44 | rawList = IPFSProc.run' ["bootstrap", "list"] 45 | ``` 46 | {% endtab %} 47 | 48 | {% tab title="Wrong" %} 49 | ```haskell 50 | rawList :: MonadRIO cfg m 51 | => Has IPFS.BinPath cfg 52 | => Has IPFS.Timeout cfg 53 | => HasProcessContext cfg 54 | => HasLogFunc cfg 55 | => m (ExitCode, Lazy.ByteString, Lazy.ByteString) 56 | rawList = IPFSProc.run' ["bootstrap", "list"] 57 | ``` 58 | {% endtab %} 59 | {% endtabs %} 60 | 61 | Why is it like this? A major reason is that the syntax highlighter in VS Code breaks with the `::` on the next line. We've also reverted to using tuple-style constraints, because in practive they making distinguishing between the constraints and argument types. 62 | 63 | ## Pipes 64 | 65 | Indent multi-line pipelines by 2 from the first item. This makes it consistent in `do`-notation and normal notations. 66 | 67 | {% tabs %} 68 | {% tab title="Right" %} 69 | ```haskell 70 | app 71 | |> condDebug 72 | |> CORS.middleware 73 | |> runner settings 74 | |> liftIO 75 | ``` 76 | {% endtab %} 77 | 78 | {% tab title="Wrong" %} 79 | ```haskell 80 | app 81 | |> condDebug 82 | |> CORS.middleware 83 | |> runner settings 84 | |> liftIO 85 | ``` 86 | {% endtab %} 87 | {% endtabs %} 88 | 89 | As much as possible, pipe in one direction 90 | 91 | {% tabs %} 92 | {% tab title="Right" %} 93 | ```haskell 94 | Hku.Password <| encodeUtf8 <| Hku.password <| Hku.api <| manifest 95 | ``` 96 | {% endtab %} 97 | 98 | {% tab title="Wrong" %} 99 | ```haskell 100 | Hku.Password <| encodeUtf8 (manifest |> Hku.api |> Hku.password) 101 | ``` 102 | {% endtab %} 103 | {% endtabs %} 104 | 105 | Break long pipelines into multiple lines where possible 106 | 107 | {% tabs %} 108 | {% tab title="Right" %} 109 | ```haskell 110 | app 111 | |> condDebug 112 | |> CORS.middleware 113 | |> runner settings 114 | |> liftIO 115 | 116 | ``` 117 | {% endtab %} 118 | 119 | {% tab title="Wrong" %} 120 | ```haskell 121 | app |> condDebug |> CORS.middleware |> runner settings |> liftIO 122 | ``` 123 | {% endtab %} 124 | {% endtabs %} 125 | 126 | Do not mix pipes with the bind operator 127 | 128 | {% tabs %} 129 | {% tab title="Right" %} 130 | ```haskell 131 | app <- Web.app 132 | 133 | app 134 | |> condDebug 135 | |> CORS.middleware 136 | |> runner settings 137 | |> liftIO 138 | ``` 139 | {% endtab %} 140 | 141 | {% tab title="Wrong" %} 142 | ```haskell 143 | Web.app 144 | >>= condDebug 145 | |> CORS.middleware 146 | |> runner settings 147 | |> liftIO 148 | ``` 149 | {% endtab %} 150 | {% endtabs %} 151 | 152 | ## Avoid the Point-Free Style 153 | 154 | Refrain from defining functions in point-free style: 155 | 156 | {% tabs %} 157 | {% tab title="Right" %} 158 | ```haskell 159 | docs :: Web.Host -> Swagger 160 | docs host' = 161 | host' 162 | |> app (Proxy @Web.API) 163 | |> dns 164 | |> ipfs 165 | |> heroku 166 | |> ping 167 | |> user 168 | ``` 169 | {% endtab %} 170 | 171 | {% tab title="Wrong" %} 172 | ```haskell 173 | docs :: Web.Host -> Swagger 174 | docs = app (Proxy @Web.API) 175 | . dns 176 | . ipfs 177 | . heroku 178 | . ping 179 | . user 180 | 181 | ``` 182 | {% endtab %} 183 | {% endtabs %} 184 | 185 | However, they often do read well in a pipeline: 186 | 187 | {% tabs %} 188 | {% tab title="Right" %} 189 | ```haskell 190 | alphaNum :: MonadIO m => Natural -> m Text 191 | alphaNum len = 192 | len 193 | |> bsRandomLength 194 | |> fmap (decodeUtf8Lenient . BS.filter isAsciiAlphaNum) 195 | ``` 196 | {% endtab %} 197 | 198 | {% tab title="Acceptable" %} 199 | ```haskell 200 | alphaNum :: MonadIO m => Natural -> m Text 201 | alphaNum len = 202 | len 203 | |> bsRandomLength 204 | |> fmap (\str -> 205 | str 206 | |> BS.filter isAsciiAlphaNum 207 | |> decodeUtf8Lenient) 208 | ``` 209 | {% endtab %} 210 | {% endtabs %} 211 | 212 | ## `MonadIO` 213 | 214 | Whenever possible, generalize `IO` functions to `MonadIO`. This prevents you from needing to generalize the function at the call site, while still able to use it in `IO` contexts. 215 | 216 | {% tabs %} 217 | {% tab title="Right" %} 218 | ```haskell 219 | fromHandler :: MonadIO m => Handler a -> m a 220 | fromHandler handler = 221 | liftIO <| runHandler handler >>= \case 222 | Right inner -> pure inner 223 | Left servantErr -> throwM servantErr 224 | 225 | -- In use... 226 | 227 | server appHost = Web.Swagger.server fromHandler appHost 228 | :<|> IPFS.server 229 | :<|> const Heroku.server 230 | :<|> User.server 231 | :<|> pure Ping.pong 232 | :<|> DNS.server 233 | ``` 234 | {% endtab %} 235 | 236 | {% tab title="Wrong" %} 237 | ```haskell 238 | fromHandler :: Handler a -> IO a 239 | fromHandler handler = 240 | runHandler handler >>= \case 241 | Right inner -> pure inner 242 | Left servantErr -> throwM servantErr 243 | 244 | -- In use... 245 | 246 | server appHost = Web.Swagger.server (liftIO . fromHandler) appHost 247 | :<|> IPFS.server 248 | :<|> const Heroku.server 249 | :<|> User.server 250 | :<|> pure Ping.pong 251 | :<|> DNS.server 252 | ``` 253 | {% endtab %} 254 | {% endtabs %} 255 | 256 | -------------------------------------------------------------------------------- /chapter-two-type-classes.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "Type classes are an indefensible part of how we write Haskell. A lot of the rest of this material won't make a lot of sense without them\U0001F92A" 3 | --- 4 | 5 | # Chapter Two: Type Classes 6 | 7 | ## What's in a Name? 8 | 9 | Calling them "type classes" is both a stroke of genius and a terrible mistake. There are absolutely strong parallels with classes from object-oriented programming. They also mostly work the exact opposite way that you'd expect coming from OOP. 10 | 11 | ## Abstraction 12 | 13 | Type classes let you define a common interface across many types. The concrete function that gets called gets determined by how you define an `instance` of that `class`. 14 | 15 | For example, let's say that you have these functions: 16 | 17 | ```haskell 18 | add :: Int -> Int -> Int 19 | add a b = a + b 20 | 21 | (++) :: [a] -> [a] -> [a] 22 | [] ++ bs = bs 23 | (a : as) ++ bs = a : as ++ bs 24 | 25 | append :: Text -> Text -> Text 26 | append a b = -- Long complex function 27 | 28 | -- Function composition 29 | (.) :: (b -> c) -> (a -> b) -> (a -> c) 30 | f . g = \x -> f (g x) 31 | ``` 32 | 33 | In a sense, all of these do the same thing: they combine values of a common type together \(i.e. they all have the form `a -> a -> a`\) While we may expect them to behave in some common ways, the exact meaning of that depends on the specific type. Why not write functions that could work on any of these types with respect to this combining behaviour? 34 | 35 | We can define a common interface for this "combinable" behaviour! 36 | 37 | ```haskell 38 | class Combinable a where 39 | combine :: a -> a -> a 40 | ``` 41 | 42 | This is called a semigroup in maths, so the version in the library is actually called `Semigroup`. Don't let the math-y name scare you! It's actually very straightforward for a "combinable" thing. 43 | 44 | ```haskell 45 | class Semigroup a where 46 | (<>) :: a -> a -> a 47 | ``` 48 | 49 | Let's write some instances! 50 | 51 | ```haskell 52 | instance Semigroup Int where 53 | a <> b = a + b 54 | 55 | instance Semigroup [a] where 56 | as <> bs = as ++ bs 57 | 58 | instance Semigroup Text where 59 | a <> b = append a b 60 | ``` 61 | 62 | Finally, let's use it in a function. We place "class constraints" before the arguments in the type signature, separated by a `=>`. 63 | 64 | ```haskell 65 | triple :: Semigroup a => a -> a 66 | triple x = x <> x <> x 67 | 68 | triple 10 69 | -- 30 70 | 71 | triple [1, 2, 3] 72 | -- [1, 2, 3, 1, 2, 3, 1, 2, 3] 73 | 74 | triple "hi" 75 | -- "hihihi" 76 | 77 | (triple (*2)) 5 78 | -- 40 79 | ``` 80 | 81 | Libraries tend to give you a lot of "functions for free" to go with your instance. It's typically worth it to define an instance for a new data type. 82 | 83 | ## Recursive Abstraction 84 | 85 | #### It's Roughly Like Inheritance™ 86 | 87 | ![](.gitbook/assets/unlimitedpower-funny-gifs.gif) 88 | 89 | Unlike OO where inheritance happens on concrete data, Haskell's "inheritance" on interfaces is more like saying "assuming that you have an `x`, then you can also do `y`!" It lets you extend abstract interfaces with more functions. This usually makes for a smaller number of types that have the new interface, because they need the old type class, plus an instance for the new one, which is not always even possible! 90 | 91 | Let's extend that Semigroup to have an element that when used with `<>` is a no op. We'll call this `mempty`, because of how it works on lists \(\[\] ++ xs = xs\) and because the name `id` was already taken. 92 | 93 | ```haskell 94 | class Semigroup a => Monoid a where 95 | mempty :: a 96 | ``` 97 | 98 | By definition, a Monoid is assumed to have a Semigroup instance kicking around. It has both `mempty` and `<>` automatically available. Let's look at a few of these: 99 | 100 | ```haskell 101 | intance Monoid Int where 102 | mempty = 0 103 | 104 | instance Monoid [a] where 105 | mempty = [] 106 | 107 | instance Monoid Text where 108 | mempty = "" 109 | 110 | instance Monoid (-> a) where 111 | mempty = id 112 | ``` 113 | 114 | ## Functor Hierarchy 115 | 116 | The most widely-used type class heirarchy is the `Functor` tree, which includes the infamour `Monad` class. These also have special rules that you need to obey when writing instances for, but you'll mostly be using someone else's so don't worry about them too much for now. 117 | 118 | There's also highly-recommended visual guide for the following material at [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html). 119 | 120 | ### Functor 121 | 122 | You may not know it, but you're already familair with functors! 123 | 124 | ```haskell 125 | class Functor where 126 | fmap :: (a -> b) -> f a -> f b 127 | ``` 128 | 129 | You'll see a lot of `fmap`'s operator variants `<$>` and `<&>` floating around 130 | 131 | ```haskell 132 | fmap show [1,2,3] -- ["1", "2", "3"] 133 | show <$> [1,2,3] -- ["1", "2", "3"] 134 | ``` 135 | 136 | The pun here is that it's the same as `$` \(application\) but working on something that's been put into a container \(practically any sum or product type\), hence the visual pun of `<$>`. Same goes for `&` \(pipe\) and `<&>`. 137 | 138 | ```haskell 139 | show $ 1 -- "1" 140 | show <$> [1, 2, 3] -- ["1", "2", "3"] 141 | 142 | 1 & show -- "1" 143 | [1, 2, 3] <&> show -- ["1", "2", "3"] 144 | ``` 145 | 146 | ### Applicative Functor 147 | 148 | `Applicative`s add a lot of power to `Functors` by defining two functions: 149 | 150 | 1. `pure` \(also known as the somewhat confusingly named `return`\) takes a plain value and wraps it in a container. 151 | 2. `<*>` does function application when both functions and arguments are wrapped 152 | 153 | ```haskell 154 | class Functor f => Applicative f where 155 | pure :: a -> f a 156 | (<*>) :: f (a -> b) -> f a -> f b 157 | ``` 158 | 159 | {% hint style="info" %} 160 | The `<*>` is often referred to as the [Tie Fighter](https://starwars.fandom.com/wiki/TIE/LN_starfighter) operator 🤣 It's also called `ap`, short for "applicative apply". 161 | {% endhint %} 162 | 163 | For example, on `Maybe`: 164 | 165 | ```haskell 166 | instance Applicative (Maybe a) where 167 | pure x = Just x 168 | 169 | Just f <*> Just x = Just $ f x 170 | _ <*> _ = Nothing 171 | 172 | pure 4 :: Just Int -- Just 4 173 | pure "hi" :: Just Text -- Just "hi" 174 | 175 | Just (*10) <*> Just 6 -- Just 60 176 | Nothing <*> Just 6 -- Nothing 177 | Just (*10) <*> Nothing -- Nothing 178 | ``` 179 | 180 | ### Monad 181 | 182 | ```haskell 183 | class Applicative m => Monad m where 184 | (>>=) :: m a -> (a -> m b) -> m b 185 | join :: m (m a) -> m a 186 | ``` 187 | 188 | {% hint style="success" %} 189 | `>>=` is pronounced "bind". This is because when you use it to link into the next function, it "binds" to the first argument, like a supercharged pipe. Further, because of how scoping rules work, argument name bindings last until the end of the chain: 190 | 191 | ```haskell 192 | [1,2,3] >>= \a -> 193 | [a + 1] >>= \b -> 194 | [(show b, a * 10)] 195 | -- [("2", 10), ("3", 20), ("4", 30)] 196 | ``` 197 | {% endhint %} 198 | 199 | {% hint style="warning" %} 200 | There's also an inverted bind operator, =<< with arguments reversed 201 | {% endhint %} 202 | 203 | Let's look at a couple instances: 204 | 205 | ```haskell 206 | instance Monad [a] where 207 | join xs = foldr (++) [] xs 208 | xs >>= f = join (f <$> xs) 209 | 210 | instance Monad (Maybe a) where 211 | join Nothing = Nothing 212 | join (Just inner) = inner 213 | 214 | Nothing >>= f = Nothing 215 | Just x >>= f = Just (f x) 216 | ``` 217 | 218 | `>>=` is special compared to `<$>` and `<*>`. It doesn't run out of arguments, and can be chained forever. 219 | 220 | ```haskell 221 | [1,2,3] >>= \a -> 222 | pure (a + 1) >>= \b -> 223 | pure (b * 10) >>= \c -> 224 | pure (c - 5) 225 | -- [15, 25, 35] 226 | ``` 227 | 228 | It can also be used to change behaviour, changing its structure based on arguments: 229 | 230 | ```haskell 231 | [1,2,3] >>= \a -> 232 | if rem a 2 == 0 then [a + 1] else [a, a * 2, a * 3] >>= \b -> 233 | if b > 10 then [a] else [b] 234 | -- [1, 2, 3, 3, 3, 6, 9] 235 | 236 | Just 42 >>= \a -> 237 | if a > 10 then Nothing else Just a >>= \b -> 238 | Just (b * 10) >>= \c -> 239 | Just (show c) 240 | -- Nothing 241 | ``` 242 | 243 | #### Do Notation 244 | 245 | Above there's a lot of `>>=` and nesting to keep it straight. Haskell has "do notation" to clean this up, make it feel like an linear set of instructions, and also pun on a lot of imperative programming. 246 | 247 | When using do notion, we often replace `pure` with `return`, which is the same function but renamed for extra imperative punning. 248 | 249 | {% hint style="danger" %} 250 | `return` does not exit out of the function chain. It is the same function as `pure` and only wraps values in the correct container. 251 | {% endhint %} 252 | 253 | {% tabs %} 254 | {% tab title="Bind" %} 255 | ```haskell 256 | [1,2,3] >>= \a -> 257 | pure (a + 1) >>= \b -> 258 | pure (b * 10) >>= \c -> 259 | pure (c - 5) 260 | ``` 261 | {% endtab %} 262 | 263 | {% tab title="Do Notation" %} 264 | ```haskell 265 | do 266 | a <- [1, 2, 3] 267 | b <- return $ a + 1 268 | c <- return $ b * 10 269 | return $ c - 5 270 | ``` 271 | {% endtab %} 272 | {% endtabs %} 273 | 274 | There's also a special variant of `let` that lets you skip the `<-` for simple values. 275 | 276 | {% tabs %} 277 | {% tab title="Without Let" %} 278 | ```haskell 279 | do 280 | a <- [1, 2, 3] 281 | b <- return (a * 10) 282 | return (b + 1) 283 | ``` 284 | {% endtab %} 285 | 286 | {% tab title="With Let" %} 287 | ```haskell 288 | do 289 | let as = [1, 2, 3] 290 | let b = fmap (*10) as 291 | return (b + 1) 292 | ``` 293 | {% endtab %} 294 | {% endtabs %} 295 | 296 | ### Cheat Sheet 297 | 298 | How these all relate to each other can be hard to keep straight at first. Here's a handy guide in pseudocode: 299 | 300 | ```haskell 301 | -- WARNING: Abuse of notation! 302 | 303 | (a -> b) $ a == b -- Plain function 304 | (a -> b) <$> m a == m b -- Functor 305 | m (a -> b) <*> m a == m b -- Applicative 306 | (a -> m b) =<< m a == m b -- Monad 307 | 308 | {-| Legend 309 | a is a value 310 | b is a value 311 | m is a data wrapper / container 312 | -} 313 | ``` 314 | 315 | ## The Orphanage 316 | 317 | For the compiler to know that there are not duplicate, conflicting instances for type classes, instances need to be written in the same module as the `class` declaration, or in the same module as a datatype definition. But what happens when you need to add an instance from a library to a datatype that's also from a library? 318 | 319 | These are called type class "orphans," and we ~~shove~~ isolate them in a module \(or submodules\) called `Fission.Internal.Orphanage`. Importing this module doesn't have any actual exports, but you do need to signal to the compiler that it's a dependency for the module that you're writing. Importing it normally will lead to the linter complaining about an unused import. You can get around this warning by explicitely showiung that are no imports: 320 | 321 | ```haskell 322 | import Fission.Internal.Orphanage () 323 | ``` 324 | 325 | The downside to having all of your orphan instances in a single module is that it progressivbley slows your compile times down after touching the orphanage because it need to check every module that imports it for new instances. As this file grows, it is typically a good idea to break it into submodules, and only import the orphan modules that you actually need in each scenario. 326 | 327 | -------------------------------------------------------------------------------- /part-i.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Translating from TypeScript to Haskell 3 | --- 4 | 5 | # Syntactic Cheat Sheet 6 | 7 | ## Basics 8 | 9 | ### Comments 10 | 11 | {% tabs %} 12 | {% tab title="TypeScript" %} 13 | ```typescript 14 | // This is a TypeScript comment 15 | 16 | /* 17 | Need more space? 18 | No problem! 19 | This is a multi-line TypeScript comment :) 20 | */ 21 | 22 | /** 23 | * In TS, you write documentation like this for TypeDoc 24 | */ 25 | const id = a => a 26 | ``` 27 | {% endtab %} 28 | 29 | {% tab title="Haskell" %} 30 | ```haskell 31 | -- This is a Haskell comment 32 | 33 | {- 34 | Need more space? 35 | No problem! 36 | This is a multi-line Haskell comment :) 37 | -} 38 | 39 | --| Add a pipe for a short function description 40 | -- You can keep talking about is afterwards 41 | -- These will turn into formatted documentation with Haddock 42 | id a = a 43 | 44 | {-| You can use a multi-line comment for Haddock, too 45 | The same rules apply. 46 | -} 47 | swap (a, b) -> (b, a) 48 | ``` 49 | {% endtab %} 50 | {% endtabs %} 51 | 52 | ### Assignment 53 | 54 | #### Implicit Type 55 | 56 | {% tabs %} 57 | {% tab title="TypeScript" %} 58 | ```typescript 59 | const hi = "Hello World" 60 | ``` 61 | {% endtab %} 62 | 63 | {% tab title="Haskell" %} 64 | ```haskell 65 | hi = "Hello World" 66 | ``` 67 | {% endtab %} 68 | {% endtabs %} 69 | 70 | #### Explicit Type 71 | 72 | {% tabs %} 73 | {% tab title="TypeScript" %} 74 | ```typescript 75 | const hi: string = 'Hello World' 76 | ``` 77 | {% endtab %} 78 | 79 | {% tab title="Haskell" %} 80 | ```haskell 81 | hi :: Text 82 | hi = "Hello World" 83 | ``` 84 | {% endtab %} 85 | 86 | {% tab title="Haskell \(Local Clarification Variant\)" %} 87 | ```haskell 88 | hi = ("Hello World" :: Text) 89 | ``` 90 | {% endtab %} 91 | {% endtabs %} 92 | 93 | ### Functions 94 | 95 | #### Anonymous 96 | 97 | {% tabs %} 98 | {% tab title="TypeScript" %} 99 | ```typescript 100 | a => a * 2 101 | ``` 102 | {% endtab %} 103 | 104 | {% tab title="Haskell" %} 105 | ```haskell 106 | \a -> a * 2 107 | -- NOTE: `\` is an easier to type lambda `λ` 108 | ``` 109 | {% endtab %} 110 | 111 | {% tab title="Haskell \(Shorthand\)" %} 112 | ```haskell 113 | (*2) 114 | ``` 115 | {% endtab %} 116 | {% endtabs %} 117 | 118 | #### Named 119 | 120 | {% tabs %} 121 | {% tab title="TypeScript" %} 122 | ```typescript 123 | const double = (a : number) => a * 2 124 | ``` 125 | {% endtab %} 126 | 127 | {% tab title="Haskell" %} 128 | ```haskell 129 | double :: Int -> Int 130 | double a = a * 2 131 | ``` 132 | {% endtab %} 133 | 134 | {% tab title="Haskell \(Shorthand\)" %} 135 | ```haskell 136 | double :: Int -> Int 137 | double = (*2) 138 | ``` 139 | {% endtab %} 140 | {% endtabs %} 141 | 142 | #### Infix 143 | 144 | {% tabs %} 145 | {% tab title="TypeScript" %} 146 | ```typescript 147 | // Does not exist 148 | ``` 149 | {% endtab %} 150 | 151 | {% tab title="Haskell" %} 152 | ```haskell 153 | (*&) :: Int -> String -> String 154 | num *& str = show num ++ str 155 | -- NOTE: `show` turns values into strings 156 | ``` 157 | {% endtab %} 158 | {% endtabs %} 159 | 160 | #### Argument Application 161 | 162 | {% tabs %} 163 | {% tab title="TypeScript" %} 164 | ```typescript 165 | const result = doTheThing(1, 2, ["a", "b"], 1.1) 166 | ``` 167 | {% endtab %} 168 | 169 | {% tab title="Haskell" %} 170 | ```haskell 171 | result = doTheThing 1 2 ["a", "b"] 1.1 172 | ``` 173 | {% endtab %} 174 | {% endtabs %} 175 | 176 | #### Side-Effectful 177 | 178 | {% tabs %} 179 | {% tab title="TypeScript" %} 180 | ```typescript 181 | fireTheMissiles() 182 | ``` 183 | {% endtab %} 184 | 185 | {% tab title="Haskell" %} 186 | ```haskell 187 | fireTheMissiles 188 | ``` 189 | {% endtab %} 190 | {% endtabs %} 191 | 192 | #### Inner Scope 193 | 194 | {% tabs %} 195 | {% tab title="TypeScript" %} 196 | ```typescript 197 | const withInner = (toLog: string): void => { 198 | const innerValue = 'That is the question!' 199 | console.log(`${toLog}? Or not ${toLog}? ${innerValue}`) 200 | } 201 | ``` 202 | {% endtab %} 203 | 204 | {% tab title="Haskell \(let...in\)" %} 205 | ```haskell 206 | withInner' toLog = 207 | let 208 | innerValue = "That is the question!" 209 | msg = toLog <> "? Or not " <> toLog <> "? " <> innerValue 210 | in 211 | logInfo msg 212 | ``` 213 | {% endtab %} 214 | 215 | {% tab title="Haskell \(where\)" %} 216 | ```haskell 217 | withInner toLog = logInfo msg 218 | where 219 | msg = toLog <> "? Or not " <> toLog <> "? " <> innerValue 220 | innerValue = "That is the question!" 221 | ``` 222 | {% endtab %} 223 | {% endtabs %} 224 | 225 | #### Nested Application 226 | 227 | {% tabs %} 228 | {% tab title="TypeScript" %} 229 | ```typescript 230 | const result = triple(prod(1, double(1), 3)) 231 | 232 | /* i.e. 233 | const two = double(1) 234 | const six = prod(1, two, 3) 235 | const result = triple(six) 236 | */ 237 | ``` 238 | {% endtab %} 239 | 240 | {% tab title="Haskell \(parens\)" %} 241 | ```haskell 242 | result = triple (prod 1 (double 1) 3) 243 | ``` 244 | {% endtab %} 245 | 246 | {% tab title="Haskell \(applied with $\)" %} 247 | ```haskell 248 | result = triple $ prod 1 (double 1) 3 249 | ``` 250 | {% endtab %} 251 | 252 | {% tab title="Haskell \(Piped with &\)" %} 253 | ```haskell 254 | result = 3 255 | & prod 1 (double 1) 256 | & triple 257 | ``` 258 | {% endtab %} 259 | {% endtabs %} 260 | 261 | #### Composition 262 | 263 | {% tabs %} 264 | {% tab title="TypeScript" %} 265 | ```typescript 266 | const timesSix = (a: number): number => triple(double(a)) 267 | ``` 268 | {% endtab %} 269 | 270 | {% tab title="Haskell" %} 271 | ```haskell 272 | timesSix :: Int -> Int 273 | timeSix = triple . double 274 | ``` 275 | {% endtab %} 276 | {% endtabs %} 277 | 278 | #### Partial Application 279 | 280 | {% tabs %} 281 | {% tab title="TypeScript" %} 282 | ```typescript 283 | const math = (n: number, m: number): number = 284 | triple(prod(10, double(1), n), m) 285 | ``` 286 | {% endtab %} 287 | 288 | {% tab title="Haskell" %} 289 | ```haskell 290 | math :: Int -> Int -> Int 291 | math n m = triple (prod 10 (double 1) n) m 292 | ``` 293 | {% endtab %} 294 | 295 | {% tab title="Haskell \(Point-Free Style\)" %} 296 | ```haskell 297 | math :: Int -> Int -> Int 298 | math = triple . prod 10 (double 1) 299 | ``` 300 | {% endtab %} 301 | {% endtabs %} 302 | 303 | ### Modules 304 | 305 | {% tabs %} 306 | {% tab title="TypeScript" %} 307 | ```typescript 308 | import * from "./GetEverything" 309 | import * as HL from "./HelperLib"; 310 | import { foo, bar } from "./SomeLib" 311 | 312 | const id = a => a 313 | const swap = ([a, b]) => [b, a] 314 | const hiddenValue = HL.downcase("not exported") 315 | 316 | export { 317 | id, 318 | swap 319 | } 320 | 321 | export { toReExport } from "./ExternalLib" 322 | ``` 323 | {% endtab %} 324 | 325 | {% tab title="Haskell" %} 326 | ```haskell 327 | module MyModule 328 | ( id 329 | , swap 330 | , toReExport 331 | ) where 332 | 333 | import GetEverything 334 | import SomeLib (foo, bar) 335 | import ExternalLib (toReExport) 336 | import qualified HelperLib as HL 337 | 338 | id a = a 339 | swap (a, b) = (b, a) 340 | hiddenValue = HL.downcase "not exported" 341 | ``` 342 | {% endtab %} 343 | {% endtabs %} 344 | 345 | ## Types & Data 346 | 347 | ### Type Alias 348 | 349 | {% tabs %} 350 | {% tab title="TypeScript" %} 351 | ```typescript 352 | type Name = string 353 | type Balance = number 354 | ``` 355 | {% endtab %} 356 | 357 | {% tab title="Haskell" %} 358 | ```haskell 359 | type Name = Text 360 | type Balance = Int 361 | ``` 362 | {% endtab %} 363 | {% endtabs %} 364 | 365 | ### Sum / Enum / Coproduct 366 | 367 | {% tabs %} 368 | {% tab title="TypeScript" %} 369 | ```typescript 370 | type TrafficLight = 'RED' | 'YELLOW' | 'GREEN' 371 | ``` 372 | {% endtab %} 373 | 374 | {% tab title="Haskell" %} 375 | ```haskell 376 | data TrafficLight 377 | = Red 378 | | Yellow 379 | | Green 380 | ``` 381 | {% endtab %} 382 | {% endtabs %} 383 | 384 | ### Product 385 | 386 | {% tabs %} 387 | {% tab title="TypeScript" %} 388 | ```typescript 389 | type GearRatio = [number, number] // [big gear, little gear] 390 | type Bike = [string, GearRatio] // [bikeName, gear ratio] 391 | ``` 392 | {% endtab %} 393 | 394 | {% tab title="Haskell" %} 395 | ```haskell 396 | data GearRatio = GearRatio Int Int 397 | data Bike = Bike Text GearRatio 398 | ``` 399 | {% endtab %} 400 | {% endtabs %} 401 | 402 | #### Interfaces & Classes → Records 403 | 404 | {% tabs %} 405 | {% tab title="TypeScript \(Interface\)" %} 406 | ```typescript 407 | interface GearRatio { 408 | big: number 409 | little: number 410 | } 411 | 412 | interface Bike { 413 | name: string 414 | gear: GearRatio 415 | } 416 | ``` 417 | {% endtab %} 418 | 419 | {% tab title="TypeScript \(Class\)" %} 420 | ```typescript 421 | class Geared { 422 | big: number 423 | little: number 424 | 425 | constructor(gbr: number, lgr: number) { 426 | big = bgr 427 | little = lgr 428 | } 429 | } 430 | 431 | class Bike extends Geared { 432 | name: string 433 | 434 | constructor(bgr: number, lgr: number, bikeName: string) { 435 | name = bikeName 436 | super(bgr, lgr) 437 | } 438 | } 439 | ``` 440 | {% endtab %} 441 | 442 | {% tab title="Haskell" %} 443 | ```haskell 444 | data Gear = GearRatio 445 | { _big :: Natural 446 | , _little :: Natural 447 | } 448 | 449 | data Bike = Bike 450 | { _name :: Text 451 | , _gear :: Gear 452 | } 453 | ``` 454 | {% endtab %} 455 | {% endtabs %} 456 | 457 | #### Constructors 458 | 459 | {% tabs %} 460 | {% tab title="TypeScript \(Direct\)" %} 461 | ```typescript 462 | const makeGear = (big, little): GearRatio => ({ big, little }) 463 | 464 | const makeBike = (big, little, name): Bike => ({ 465 | name, 466 | gear = makeGear(big, little) 467 | }) 468 | ``` 469 | {% endtab %} 470 | 471 | {% tab title="TypeScript \(Class\)" %} 472 | ```typescript 473 | /* 474 | Included in the `class` delcaration. For example: 475 | 476 | constructor(bgr: number, lgr: number) { 477 | big = bgr 478 | little = lgr 479 | } 480 | */ 481 | ``` 482 | {% endtab %} 483 | 484 | {% tab title="Haskell" %} 485 | ```haskell 486 | {- 487 | Constructors are the capitalized word on 488 | the right of the `=` in the `data` declaration 489 | It auto-generates constructor functions 490 | with the folling type signatures: 491 | 492 | GearRatio :: Natural -> Natural -> Geared 493 | Bike :: Text -> GearRatio -> Bike 494 | -} 495 | ``` 496 | {% endtab %} 497 | {% endtabs %} 498 | 499 | #### Instantiation 500 | 501 | {% tabs %} 502 | {% tab title="TypeScript \(Direct\)" %} 503 | ```typescript 504 | const myBike = makeBike(2, 5, '10 Speeder') 505 | ``` 506 | {% endtab %} 507 | 508 | {% tab title="TypeScript \(Class\)" %} 509 | ```typescript 510 | const myBike = new Bike(2, 5, '10 Speeder') 511 | ``` 512 | {% endtab %} 513 | 514 | {% tab title="Haskell" %} 515 | ```haskell 516 | myBike = Bike "10 Speeder" (GearRatio 2 5) 517 | ``` 518 | {% endtab %} 519 | 520 | {% tab title="Haskell \(Record Style\)" %} 521 | ```haskell 522 | myBike = Bike 523 | { _name = "10 Speeder" -- Use named fields 524 | , _gear = GearRatio 2 5 -- Or just ordered values 525 | } 526 | ``` 527 | {% endtab %} 528 | 529 | {% tab title="Haskell \(Wildcard Style\)" %} 530 | ```haskell 531 | myBike :: Bike 532 | myBike = 533 | let 534 | _big = 2 535 | _little = 5 536 | _name = "10 Speeder" 537 | _gear = GearRatio {..} 538 | in 539 | Bike {..} 540 | ``` 541 | {% endtab %} 542 | {% endtabs %} 543 | 544 | ### Getters 545 | 546 | {% tabs %} 547 | {% tab title="TypeScript \(Dot Syntax\)" %} 548 | ```typescript 549 | const big: number = myGears.big 550 | const little = myBike.gear.little 551 | ``` 552 | {% endtab %} 553 | 554 | {% tab title="TypeScript \(Destructuring\)" %} 555 | ```typescript 556 | const { bigGear } = myGears 557 | const getBig = ({ big }) => big 558 | const ratio = ({ big, little }) => big / little 559 | ``` 560 | {% endtab %} 561 | 562 | {% tab title="Haskell \(Destructuring\)" %} 563 | ```haskell 564 | GearRatio big _ = myGears 565 | getBig (GearRatio {_big}) = _big 566 | ratio (GearRatio {_big, _little}) = _big / _little 567 | ``` 568 | {% endtab %} 569 | 570 | {% tab title="Haskell \(Wildcard Style\)" %} 571 | ```haskell 572 | ratio (GearRatio {..}) = _big / _little 573 | ``` 574 | {% endtab %} 575 | 576 | {% tab title="Haskell \(Lenses\)" %} 577 | ```haskell 578 | import Control.Lens.TH 579 | 580 | $(makeLenses ''Gear) 581 | $(makeLenses ''Bike) 582 | 583 | myGear ^. big -- NOTE: lenses have no underscores! 584 | 585 | -- Nested 586 | 587 | myBike ^. gear . little 588 | ``` 589 | {% endtab %} 590 | {% endtabs %} 591 | 592 | ### Updating with Setters 593 | 594 | {% tabs %} 595 | {% tab title="TypeScript" %} 596 | ```typescript 597 | // Preamble 598 | 599 | const myGear = new GearRatio(2, 5) 600 | const myBike = new Bike('10 Speeder', myGear) 601 | 602 | // Mutable Update 603 | 604 | myGear.littleGear = 6 605 | myBike.name = '12 Speeder' 606 | 607 | // Immutable Update 608 | 609 | const upgradedGear = Object.assign({}, myGear) 610 | const upgradedBike = Object.assign({}, myBike, {gear: upgradedGear}) 611 | ``` 612 | {% endtab %} 613 | 614 | {% tab title="Haskell \(Record Syntax\)" %} 615 | ```haskell 616 | -- Preamble 617 | 618 | myGear = GearRatio 2 5 619 | myBike = Bike "10 Speeder" myGear 620 | 621 | -- Immutable Update 622 | 623 | upgradedBike = myBike 624 | { _name = "12 Speeder" 625 | , _gear = myGear { _littleGear = 6 } 626 | } 627 | ``` 628 | {% endtab %} 629 | 630 | {% tab title="Haskell \(Lenses\)" %} 631 | ```haskell 632 | -- Preamble 633 | 634 | import Control.Lens.TH 635 | 636 | makeLenses ''Gear 637 | makeLenses ''Bike 638 | 639 | myGear = GearRatio 2 5 640 | myBike = Bike "10 Speeder" myGear 641 | 642 | -- Immutable Update 643 | 644 | upgradedBike = myBike & name .~ "12 Speeder" 645 | & gear.littleGear .~ 6 646 | ``` 647 | {% endtab %} 648 | {% endtabs %} 649 | 650 | ## Branching 651 | 652 | ### If...Else 653 | 654 | {% tabs %} 655 | {% tab title="TypeScript" %} 656 | ```typescript 657 | if (condition) { 658 | branchA 659 | } else { 660 | branchB 661 | } 662 | ``` 663 | {% endtab %} 664 | 665 | {% tab title="TypeScript Ternary" %} 666 | ```typescript 667 | condition ? branchA : branchB 668 | ``` 669 | {% endtab %} 670 | 671 | {% tab title="Haskell" %} 672 | ```haskell 673 | if condition 674 | then branchA 675 | else branchB 676 | ``` 677 | {% endtab %} 678 | {% endtabs %} 679 | 680 | ### If...Else If...Else 681 | 682 | {% tabs %} 683 | {% tab title="TypeScript" %} 684 | ```typescript 685 | if (percent >= 90 || student.paidBribe) { 686 | return GRADES.A 687 | } else if (percent >= 75) { 688 | return GRADES.B 689 | } else if (percent >= 60) { 690 | return GRADES.C 691 | } else if (percent >= 50) { 692 | return GRADES.D 693 | } else { 694 | return GRADES.F 695 | } 696 | ``` 697 | {% endtab %} 698 | 699 | {% tab title="Haskell" %} 700 | ```haskell 701 | if | percent >= 90 || paidBribe student = A 702 | | percent >= 75 = B 703 | | percent >= 60 = C 704 | | percent >= 50 = D 705 | | otherwise = F 706 | ``` 707 | {% endtab %} 708 | {% endtabs %} 709 | 710 | ### Switch/Case 711 | 712 | {% tabs %} 713 | {% tab title="TypeScript" %} 714 | ```typescript 715 | let nextAction 716 | 717 | switch (lightState) { 718 | case LIGHT.RED: 719 | nextAction = ACTION.STOP 720 | break 721 | 722 | case LIGHT.YELLOW: 723 | nextAction = ACTION.SLOW 724 | break 725 | 726 | case LIGHT.GREEN: 727 | nextAction = ACTION.DRIVE 728 | break 729 | 730 | default: 731 | nextAction = currentAction 732 | } 733 | ``` 734 | {% endtab %} 735 | 736 | {% tab title="Haskell" %} 737 | ```haskell 738 | nextAction = 739 | case lightState of 740 | Red -> Stop 741 | Yellow -> Slow 742 | Green -> Drive 743 | _ -> currentAction 744 | ``` 745 | {% endtab %} 746 | {% endtabs %} 747 | 748 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-ShareAlike 4.0 International Public 58 | License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-ShareAlike 4.0 International Public License ("Public 63 | License"). To the extent this Public License may be interpreted as a 64 | contract, You are granted the Licensed Rights in consideration of Your 65 | acceptance of these terms and conditions, and the Licensor grants You 66 | such rights in consideration of benefits the Licensor receives from 67 | making the Licensed Material available under these terms and 68 | conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. Share means to provide material to the public by any means or 126 | process that requires permission under the Licensed Rights, such 127 | as reproduction, public display, public performance, distribution, 128 | dissemination, communication, or importation, and to make material 129 | available to the public including in ways that members of the 130 | public may access the material from a place and at a time 131 | individually chosen by them. 132 | 133 | l. Sui Generis Database Rights means rights other than copyright 134 | resulting from Directive 96/9/EC of the European Parliament and of 135 | the Council of 11 March 1996 on the legal protection of databases, 136 | as amended and/or succeeded, as well as other essentially 137 | equivalent rights anywhere in the world. 138 | 139 | m. You means the individual or entity exercising the Licensed Rights 140 | under this Public License. Your has a corresponding meaning. 141 | 142 | 143 | Section 2 -- Scope. 144 | 145 | a. License grant. 146 | 147 | 1. Subject to the terms and conditions of this Public License, 148 | the Licensor hereby grants You a worldwide, royalty-free, 149 | non-sublicensable, non-exclusive, irrevocable license to 150 | exercise the Licensed Rights in the Licensed Material to: 151 | 152 | a. reproduce and Share the Licensed Material, in whole or 153 | in part; and 154 | 155 | b. produce, reproduce, and Share Adapted Material. 156 | 157 | 2. Exceptions and Limitations. For the avoidance of doubt, where 158 | Exceptions and Limitations apply to Your use, this Public 159 | License does not apply, and You do not need to comply with 160 | its terms and conditions. 161 | 162 | 3. Term. The term of this Public License is specified in Section 163 | 6(a). 164 | 165 | 4. Media and formats; technical modifications allowed. The 166 | Licensor authorizes You to exercise the Licensed Rights in 167 | all media and formats whether now known or hereafter created, 168 | and to make technical modifications necessary to do so. The 169 | Licensor waives and/or agrees not to assert any right or 170 | authority to forbid You from making technical modifications 171 | necessary to exercise the Licensed Rights, including 172 | technical modifications necessary to circumvent Effective 173 | Technological Measures. For purposes of this Public License, 174 | simply making modifications authorized by this Section 2(a) 175 | (4) never produces Adapted Material. 176 | 177 | 5. Downstream recipients. 178 | 179 | a. Offer from the Licensor -- Licensed Material. Every 180 | recipient of the Licensed Material automatically 181 | receives an offer from the Licensor to exercise the 182 | Licensed Rights under the terms and conditions of this 183 | Public License. 184 | 185 | b. Additional offer from the Licensor -- Adapted Material. 186 | Every recipient of Adapted Material from You 187 | automatically receives an offer from the Licensor to 188 | exercise the Licensed Rights in the Adapted Material 189 | under the conditions of the Adapter's License You apply. 190 | 191 | c. No downstream restrictions. You may not offer or impose 192 | any additional or different terms or conditions on, or 193 | apply any Effective Technological Measures to, the 194 | Licensed Material if doing so restricts exercise of the 195 | Licensed Rights by any recipient of the Licensed 196 | Material. 197 | 198 | 6. No endorsement. Nothing in this Public License constitutes or 199 | may be construed as permission to assert or imply that You 200 | are, or that Your use of the Licensed Material is, connected 201 | with, or sponsored, endorsed, or granted official status by, 202 | the Licensor or others designated to receive attribution as 203 | provided in Section 3(a)(1)(A)(i). 204 | 205 | b. Other rights. 206 | 207 | 1. Moral rights, such as the right of integrity, are not 208 | licensed under this Public License, nor are publicity, 209 | privacy, and/or other similar personality rights; however, to 210 | the extent possible, the Licensor waives and/or agrees not to 211 | assert any such rights held by the Licensor to the limited 212 | extent necessary to allow You to exercise the Licensed 213 | Rights, but not otherwise. 214 | 215 | 2. Patent and trademark rights are not licensed under this 216 | Public License. 217 | 218 | 3. To the extent possible, the Licensor waives any right to 219 | collect royalties from You for the exercise of the Licensed 220 | Rights, whether directly or through a collecting society 221 | under any voluntary or waivable statutory or compulsory 222 | licensing scheme. In all other cases the Licensor expressly 223 | reserves any right to collect such royalties. 224 | 225 | 226 | Section 3 -- License Conditions. 227 | 228 | Your exercise of the Licensed Rights is expressly made subject to the 229 | following conditions. 230 | 231 | a. Attribution. 232 | 233 | 1. If You Share the Licensed Material (including in modified 234 | form), You must: 235 | 236 | a. retain the following if it is supplied by the Licensor 237 | with the Licensed Material: 238 | 239 | i. identification of the creator(s) of the Licensed 240 | Material and any others designated to receive 241 | attribution, in any reasonable manner requested by 242 | the Licensor (including by pseudonym if 243 | designated); 244 | 245 | ii. a copyright notice; 246 | 247 | iii. a notice that refers to this Public License; 248 | 249 | iv. a notice that refers to the disclaimer of 250 | warranties; 251 | 252 | v. a URI or hyperlink to the Licensed Material to the 253 | extent reasonably practicable; 254 | 255 | b. indicate if You modified the Licensed Material and 256 | retain an indication of any previous modifications; and 257 | 258 | c. indicate the Licensed Material is licensed under this 259 | Public License, and include the text of, or the URI or 260 | hyperlink to, this Public License. 261 | 262 | 2. You may satisfy the conditions in Section 3(a)(1) in any 263 | reasonable manner based on the medium, means, and context in 264 | which You Share the Licensed Material. For example, it may be 265 | reasonable to satisfy the conditions by providing a URI or 266 | hyperlink to a resource that includes the required 267 | information. 268 | 269 | 3. If requested by the Licensor, You must remove any of the 270 | information required by Section 3(a)(1)(A) to the extent 271 | reasonably practicable. 272 | 273 | b. ShareAlike. 274 | 275 | In addition to the conditions in Section 3(a), if You Share 276 | Adapted Material You produce, the following conditions also apply. 277 | 278 | 1. The Adapter's License You apply must be a Creative Commons 279 | license with the same License Elements, this version or 280 | later, or a BY-SA Compatible License. 281 | 282 | 2. You must include the text of, or the URI or hyperlink to, the 283 | Adapter's License You apply. You may satisfy this condition 284 | in any reasonable manner based on the medium, means, and 285 | context in which You Share Adapted Material. 286 | 287 | 3. You may not offer or impose any additional or different terms 288 | or conditions on, or apply any Effective Technological 289 | Measures to, Adapted Material that restrict exercise of the 290 | rights granted under the Adapter's License You apply. 291 | 292 | 293 | Section 4 -- Sui Generis Database Rights. 294 | 295 | Where the Licensed Rights include Sui Generis Database Rights that 296 | apply to Your use of the Licensed Material: 297 | 298 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 299 | to extract, reuse, reproduce, and Share all or a substantial 300 | portion of the contents of the database; 301 | 302 | b. if You include all or a substantial portion of the database 303 | contents in a database in which You have Sui Generis Database 304 | Rights, then the database in which You have Sui Generis Database 305 | Rights (but not its individual contents) is Adapted Material, 306 | 307 | including for purposes of Section 3(b); and 308 | c. You must comply with the conditions in Section 3(a) if You Share 309 | all or a substantial portion of the contents of the database. 310 | 311 | For the avoidance of doubt, this Section 4 supplements and does not 312 | replace Your obligations under this Public License where the Licensed 313 | Rights include other Copyright and Similar Rights. 314 | 315 | 316 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 317 | 318 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 319 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 320 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 321 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 322 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 323 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 324 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 325 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 326 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 327 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 328 | 329 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 330 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 331 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 332 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 333 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 334 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 335 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 336 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 337 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 338 | 339 | c. The disclaimer of warranties and limitation of liability provided 340 | above shall be interpreted in a manner that, to the extent 341 | possible, most closely approximates an absolute disclaimer and 342 | waiver of all liability. 343 | 344 | 345 | Section 6 -- Term and Termination. 346 | 347 | a. This Public License applies for the term of the Copyright and 348 | Similar Rights licensed here. However, if You fail to comply with 349 | this Public License, then Your rights under this Public License 350 | terminate automatically. 351 | 352 | b. Where Your right to use the Licensed Material has terminated under 353 | Section 6(a), it reinstates: 354 | 355 | 1. automatically as of the date the violation is cured, provided 356 | it is cured within 30 days of Your discovery of the 357 | violation; or 358 | 359 | 2. upon express reinstatement by the Licensor. 360 | 361 | For the avoidance of doubt, this Section 6(b) does not affect any 362 | right the Licensor may have to seek remedies for Your violations 363 | of this Public License. 364 | 365 | c. For the avoidance of doubt, the Licensor may also offer the 366 | Licensed Material under separate terms or conditions or stop 367 | distributing the Licensed Material at any time; however, doing so 368 | will not terminate this Public License. 369 | 370 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 371 | License. 372 | 373 | 374 | Section 7 -- Other Terms and Conditions. 375 | 376 | a. The Licensor shall not be bound by any additional or different 377 | terms or conditions communicated by You unless expressly agreed. 378 | 379 | b. Any arrangements, understandings, or agreements regarding the 380 | Licensed Material not stated herein are separate from and 381 | independent of the terms and conditions of this Public License. 382 | 383 | 384 | Section 8 -- Interpretation. 385 | 386 | a. For the avoidance of doubt, this Public License does not, and 387 | shall not be interpreted to, reduce, limit, restrict, or impose 388 | conditions on any use of the Licensed Material that could lawfully 389 | be made without permission under this Public License. 390 | 391 | b. To the extent possible, if any provision of this Public License is 392 | deemed unenforceable, it shall be automatically reformed to the 393 | minimum extent necessary to make it enforceable. If the provision 394 | cannot be reformed, it shall be severed from this Public License 395 | without affecting the enforceability of the remaining terms and 396 | conditions. 397 | 398 | c. No term or condition of this Public License will be waived and no 399 | failure to comply consented to unless expressly agreed to by the 400 | Licensor. 401 | 402 | d. Nothing in this Public License constitutes or may be interpreted 403 | as a limitation upon, or waiver of, any privileges and immunities 404 | that apply to the Licensor or You, including from the legal 405 | processes of any jurisdiction or authority. 406 | 407 | 408 | ======================================================================= 409 | 410 | Creative Commons is not a party to its public 411 | licenses. Notwithstanding, Creative Commons may elect to apply one of 412 | its public licenses to material it publishes and in those instances 413 | will be considered the “Licensor.” The text of the Creative Commons 414 | public licenses is dedicated to the public domain under the CC0 Public 415 | Domain Dedication. Except for the limited purpose of indicating that 416 | material is shared under a Creative Commons public license or as 417 | otherwise permitted by the Creative Commons policies published at 418 | creativecommons.org/policies, Creative Commons does not authorize the 419 | use of the trademark "Creative Commons" or any other trademark or logo 420 | of Creative Commons without its prior written consent including, 421 | without limitation, in connection with any unauthorized modifications 422 | to any of its public licenses or any other arrangements, 423 | understandings, or agreements concerning use of licensed material. For 424 | the avoidance of doubt, this paragraph does not form part of the 425 | public licenses. 426 | 427 | Creative Commons may be contacted at creativecommons.org. 428 | --------------------------------------------------------------------------------