Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# shellcheck shell=bash
use flake
16 changes: 0 additions & 16 deletions .github/workflows/nodejs.yml

This file was deleted.

53 changes: 53 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Test

on:
- push

jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Install Nix
uses: nixbuild/nix-quick-install-action@63ca48f939ee3b8d835f4126562537df0fee5b91
- name: Cache Nix store
uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a
with:
# The key includes the os/arch pair as artifacts in the Nix store are platform-specific.
primary-key: nix-${{ format('{0}_{1}', runner.os, runner.arch) }}-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}
restore-prefixes-first-match: |
nix-${{ format('{0}_{1}', runner.os, runner.arch) }}-${{ hashFiles('flake.lock') }}-
nix-${{ format('{0}_{1}', runner.os, runner.arch) }}-
- name: Cache node_modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
with:
# The key includes the os/arch pair as artifacts in `node_modules` can be platform-specific.
key: node_modules-${{ format('{0}_{1}', runner.os, runner.arch) }}-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-${{ hashFiles('**/package-lock.json') }}
path: "**/node_modules"
restore-keys: |
node_modules-${{ format('{0}_{1}', runner.os, runner.arch) }}-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-
node_modules-${{ format('{0}_{1}', runner.os, runner.arch) }}-${{ hashFiles('flake.lock') }}-
node_modules-${{ format('{0}_{1}', runner.os, runner.arch) }}-
- name: Cache bower_components
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
with:
key: bower_components-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('bower.json') }}
path: bower_components
restore-keys: |
bower_components-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-${{ hashFiles('**/package-lock.json') }}-
bower_components-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-
bower_components-${{ hashFiles('flake.lock') }}-
bower_components-
- name: Cache output
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
with:
key: output-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('bower.json') }}-${{ hashFiles('**/*.js', '**/*.purs') }}
path: output
restore-keys: |
output-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('bower.json') }}-
output-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-${{ hashFiles('**/package-lock.json') }}-
output-${{ hashFiles('flake.lock') }}-${{ hashFiles('**/*.nix') }}-
output-${{ hashFiles('flake.lock') }}-
output-
- name: Test
run: nix develop . --command make test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
### Project-specific ###

.direnv
.jj
.psc-ide-port

################################################################################
Expand Down
10 changes: 10 additions & 0 deletions .tidyrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"importSort": "source",
"importWrap": "source",
"indent": 2,
"operatorsFile": null,
"ribbon": 1,
"typeArrowPlacement": "last",
"unicode": "never",
"width": null
}
3 changes: 3 additions & 0 deletions .yamlfmt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
formatter:
retain_line_breaks_single: true
type: basic
19 changes: 11 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
BOWER := npx bower
BOWER_FLAGS ?=
COMPILE_FLAGS ?=
COMPILE_FLAGS ?= --censor-lib
DEPENDENCIES := 'bower_components/purescript-*/src/**/*.purs'
NIX := nix
NODE := node
NPM := npm
OUTPUT := output
PSA := npx psa
PURS := npx purs
PURTY := npx purty
REPL_FLAGS ?=
SRC := src
TEST := test
Expand All @@ -19,12 +19,12 @@ TEST_OUTPUTS := $(patsubst $(TEST).%.purs,$(OUTPUT)/%/index.js,$(subst /,.,$(TES

define SRC_OUTPUT_RULE
$(patsubst $(SRC).%.purs,$(OUTPUT)/%/index.js,$(subst /,.,$(1))): $(1) bower_components
$(PSA) compile $(COMPILE_FLAGS) $(DEPENDENCIES) $(SRCS)
$(PSA) $(COMPILE_FLAGS) $(DEPENDENCIES) $(SRCS)
endef

define TEST_OUTPUT_RULE
$(patsubst $(TEST).%.purs,$(OUTPUT)/%/index.js,$(subst /,.,$(1))): $(1) $(SRC_OUTPUTS) bower_components
$(PSA) compile $(COMPILE_FLAGS) $(DEPENDENCIES) $(SRCS) $(TESTS)
$(PSA) $(COMPILE_FLAGS) $(DEPENDENCIES) $(SRCS) $(TESTS)
endef

$(foreach source, $(SRCS), $(eval $(call SRC_OUTPUT_RULE, $(source))))
Expand Down Expand Up @@ -61,9 +61,12 @@ clean:
output

.PHONY: format
format: node_modules
find $(SRC) -name '*.purs' -exec $(PURTY) --write {} \;
find $(TEST) -name '*.purs' -exec $(PURTY) --write {} \;
format:
$(NIX) fmt

.PHONE: lint
lint:
$(NIX) flake check

node_modules: package.json
$(NPM) install
Expand All @@ -74,7 +77,7 @@ repl: bower_components
$(PURS) repl $(REPL_FLAGS) $(DEPENDENCIES) $(SRCS)

.PHONY: test
test: $(OUTPUT)/test.js bower_components $(SRC_OUTPUTS) $(TEST_OUTPUTS)
test: $(OUTPUT)/test.js bower_components $(SRC_OUTPUTS) $(TEST_OUTPUTS) lint
$(NODE) $<

.PHONY: variables
Expand Down
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ A data type for optional values.

## Table of Contents

* [Explanation: Motivation for `Option _`](#explanation-motivation-for-option-_)
* [How To: Make a function with optional values](#how-to-make-a-function-with-optional-values)
* [How To: Make a function with optional values from a record](#how-to-make-a-function-with-optional-values-from-a-record)
* [How To: Make a function with required and optional values](#how-to-make-a-function-with-required-and-optional-values)
* [How To: Make a function with required and optional values from a record](#how-to-make-a-function-with-required-and-optional-values-from-a-record)
* [How To: Decode and Encode JSON with optional values in `purescript-argonaut`](#how-to-decode-and-encode-json-with-optional-values-in-purescript-argonaut)
* [How To: Decode and Encode JSON with optional values in `purescript-codec-argonaut`](#how-to-decode-and-encode-json-with-optional-values-in-purescript-codec-argonaut)
* [How To: Decode and Encode JSON with optional values in `purescript-simple-json`](#how-to-decode-and-encode-json-with-optional-values-in-purescript-simple-json)
* [How To: Decode and Encode JSON with required and optional values in `purescript-argonaut`](#how-to-decode-and-encode-json-with-required-and-optional-values-in-purescript-argonaut)
* [How To: Decode and Encode JSON with required and optional values in `purescript-codec-argonaut`](#how-to-decode-and-encode-json-with-required-and-optional-values-in-purescript-codec-argonaut)
* [How To: Decode and Encode JSON with required and optional values in `purescript-simple-json`](#how-to-decode-and-encode-json-with-required-and-optional-values-in-purescript-simple-json)
* [How To: Provide an easier API for `DateTime`](#how-to-provide-an-easier-api-for-datetime)
* [Reference: `FromRecord _ _ _`](#reference-fromrecord-_-_-_)
- [Explanation: Motivation for `Option _`](#explanation-motivation-for-option-_)
- [How To: Make a function with optional values](#how-to-make-a-function-with-optional-values)
- [How To: Make a function with optional values from a record](#how-to-make-a-function-with-optional-values-from-a-record)
- [How To: Make a function with required and optional values](#how-to-make-a-function-with-required-and-optional-values)
- [How To: Make a function with required and optional values from a record](#how-to-make-a-function-with-required-and-optional-values-from-a-record)
- [How To: Decode and Encode JSON with optional values in `purescript-argonaut`](#how-to-decode-and-encode-json-with-optional-values-in-purescript-argonaut)
- [How To: Decode and Encode JSON with optional values in `purescript-codec-argonaut`](#how-to-decode-and-encode-json-with-optional-values-in-purescript-codec-argonaut)
- [How To: Decode and Encode JSON with optional values in `purescript-simple-json`](#how-to-decode-and-encode-json-with-optional-values-in-purescript-simple-json)
- [How To: Decode and Encode JSON with required and optional values in `purescript-argonaut`](#how-to-decode-and-encode-json-with-required-and-optional-values-in-purescript-argonaut)
- [How To: Decode and Encode JSON with required and optional values in `purescript-codec-argonaut`](#how-to-decode-and-encode-json-with-required-and-optional-values-in-purescript-codec-argonaut)
- [How To: Decode and Encode JSON with required and optional values in `purescript-simple-json`](#how-to-decode-and-encode-json-with-required-and-optional-values-in-purescript-simple-json)
- [How To: Provide an easier API for `DateTime`](#how-to-provide-an-easier-api-for-datetime)
- [Reference: `FromRecord _ _ _`](#reference-fromrecord-_-_-_)

## Explanation: Motivation for `Option _`

Expand Down Expand Up @@ -89,6 +89,7 @@ With the `greeting` function, we can pass in an option and alter the behavior:
We've allowed people to override the behavior of the function with optional values!

It might be instructive to compare how we might write a similar function using a `Record _` instead of `Option _`:

```PureScript
greeting' ::
Record ( name :: Data.Maybe.Maybe String, title :: Data.Maybe.Maybe String ) ->
Expand All @@ -110,6 +111,7 @@ To implement `greeting'`, nothing really changed.
We used the built-in dot operator to fetch the keys out of the record, but we could have just as easily used `Record.get` (which would have highlighted the similarlities even more).

To use `greeting'`, we force the users of `greeting'` to do always give us a value in the record:

```PureScript
> User.greeting' { name: Data.Maybe.Nothing, title: Data.Maybe.Nothing }
"Hello, World"
Expand Down Expand Up @@ -215,6 +217,7 @@ With the `greeting` function, we can pass in an option and alter the behavior:
We've allowed people to override the behavior of the function with optional values!

It might be instructive to compare how we might write a similar function using a `Record _` instead of `Option _`:

```PureScript
greeting' ::
Record ( name :: String, title :: Data.Maybe.Maybe String ) ->
Expand All @@ -232,6 +235,7 @@ We don't have to convert down to a language-level Record because the argument is
That's the only difference as far as implementing `greeting'`.

To use `greeting'`, we force the users of `greeting'` to do always give us a value in the record:

```PureScript
> User.greeting' { name: "Pat", title: Data.Maybe.Nothing }
"Hello, Pat"
Expand Down Expand Up @@ -318,6 +322,7 @@ parse string = case Data.Argonaut.Parser.jsonParser string of
```

We can give that a spin with some different JSON values:

```PureScript
> parse """{}"""
(Right (Option.fromRecord {}))
Expand Down Expand Up @@ -416,6 +421,7 @@ Unless both fields exist, we cannot decode the JSON object.
Similarly, no matter what the values are, we always encode them into a JSON object.

In order to emulate the behavior of an optional field, we have to name the record, and write our own instances:

```PureScript
newtype Greeting
= Greeting
Expand Down Expand Up @@ -834,6 +840,7 @@ writeJSON = Simple.JSON.writeJSON
```

We can give that a spin with some different JSON values:

```PureScript
> readJSON """{}"""
(Right (Option.fromRecord {}))
Expand Down Expand Up @@ -962,6 +969,7 @@ parse string = case Data.Argonaut.Parser.jsonParser string of
```

We can give that a spin with some different JSON values:

```PureScript
> parse """{}"""
(Left "An error occurred while decoding a JSON value:\n At object key 'name':\n No value was found.")
Expand Down Expand Up @@ -1050,6 +1058,7 @@ Unless both fields exist, we cannot decode the JSON object.
Similarly, no matter what the values are, we always encode them into a JSON object.

In order to emulate the behavior of an optional field, we have to name the record, and write our own instances:

```PureScript
import Prelude
import Data.Argonaut.Core as Data.Argonaut.Core
Expand Down Expand Up @@ -1495,6 +1504,7 @@ writeJSON = Simple.JSON.writeJSON
```

We can give that a spin with some different JSON values:

```PureScript
> parse """{}"""
(Left "Error at property \"name\": Type mismatch: expected String, found Undefined")
Expand Down Expand Up @@ -1585,6 +1595,7 @@ For instance, constructing a `Data.DateTime.DateTime` can be done by passing in
The issue is, how do we construct a `Data.Date.Date` or `Data.Time.Time`.

One way to get construct these values is to use the `Data.Enum.Enum` instance for both of them:

```PureScript
> Data.DateTime.DateTime bottom bottom
(DateTime (Date (Year -271820) January (Day 1)) (Time (Hour 0) (Minute 0) (Second 0) (Millisecond 0)))
Expand Down Expand Up @@ -1731,10 +1742,13 @@ E.g. `FromRecord () () ( name :: String )` says that the `Record ()` has no fiel
Since there is syntax for creating records, but no syntax for creating options, this typeclass can be useful for providing an easier to use interface to options.

E.g. Someone can say:

```PureScript
Option.fromRecord' { foo: true, bar: 31 }
```

Instead of having to say:

```PureScript
Option.insert
(Data.Symbol.SProxy :: _ "foo")
Expand Down
Loading