Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Add sumtype mutation pattern #1036

Closed
wants to merge 1 commit into from
Closed
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
37 changes: 37 additions & 0 deletions docs/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,40 @@ ownership and is deleted when they are no longer needed. Not all types are
managed. `Int`, for example, is not a managed type, and so the issue described
above won't be relevant for `Int` arguments. For more information see
[docs/memory.md](docs/memory.md)

## Update a Sumtype Reference in-place

Product types automatically generate *mutating setters*, which provide a nice
means of updating their fields in-place, given you have a reference to an
instance you want to update:

```clojure
;; An example of a mutating setter, `Pair.set-a!`
Pair.set-a!: (Fn [(Ref (Pair a b) q), a] ())
```

Contrarily, sumtypes don't generate such mutating setters, and naturally don't
lend themselves to such functions; instead, they are primarily copied around and
passed as values.

However, if you find yourself in the rare position in which you'd like to alter
the value of a Sumtype reference in-place, you can use an *unsafe coercion to a
pointer* to do so. The steps are as follows:

- Get the reference to the sumtype value you'd like to mutate
- Use `Unsafe.coerce` to coerce the reference to a `Ptr` to the value.
- Use `Pointer.set` to set the value of the pointer to a new value of the same
type.

Here's the pattern in action:

```clojure
(defn up-to-eleven [val]
(let [val* (the (Ptr (Maybe Int)) (Unsafe.coerce &val))]
Copy link
Contributor

@TimDeve TimDeve Nov 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this line be:

(let [m* (the (Ptr (Maybe Int)) (Unsafe.coerce val))]

Or more "safely":

  (let [reference (the (Ref (Maybe Int)) val)
        m* (the (Ptr (Maybe Int)) (Unsafe.coerce reference))]

(Pointer.set m* (Maybe.Just 11))))

(let-do [volume (Maybe.Just 0)]
(up-to-eleven &volume)
volume)
;; => (Just 11)
```