This commit is contained in:
Alexander 2025-08-10 11:54:46 -04:00
parent 8e9455cab1
commit 6808af8471
4 changed files with 22 additions and 51 deletions

View file

@ -7,9 +7,11 @@
"Brugrock",
"Candlehead",
"Carmal",
"Cheshira",
"Chieftess",
"Chondalwood",
"Chondath",
"Cogyth",
"Datalog",
"Deathrun",
"demi",
@ -68,6 +70,7 @@
"scrying",
"Silverhand",
"teleporter",
"Themberchaud",
"Toril",
"Underdark",
"unglimpsed",

View file

@ -1,20 +1,26 @@
# Umbral Gaze 4: The Candlehead
# Umbral Gaze 4: Candle-head
Portal network to Neverwinter.
Do some shopping and get some rest. Clementine buys a gilded acorns; Gottlob buys a diamond worth 300 gp.
The next morning, at Tasha's behest, the Wintershield Watchmen[^ww] brief our heroes about the Cake Pile.
The next morning, at Tasha's behest, the Wintershield Watchmen[^ww] brief our heroes about the Cake Pile. The dwarves who explain call it Brog Burgest, "cake pile", because to call the travesty a mountain would be an insult to mountains.
*what do they say?*
{.thematic}
***
The party has reached the base of the cake pile. There is something like a path, and Almuth does detect traps on it. He finds some, but we walk up anyway and are not struck. Louisa flies all the way up to the top, stumbling upon an otherworldly scenes that seems to bleed into reality from somwhere else. a keep is surrounded by farms of blue grain and redcap slave masters grave dead-eyed humans in the fields. Lousisa sees her form among them. It looks lik "______". She spits on a redcap,k the one with her body, and is thrown from the mountaintop by ab apparition of her curser. Clementine casts "pass without trace", so most of the party is quite sneaky. Alm. and Got. dance with 4 frosting thralls after she lands, totally unharmed, and gottlob is very graceful, for some reason. Almuth too eventually gets free. The rest of the party snuck past.
A warm, sweet breeze like the scent from a bakers oven tousles the party's hair and delights their nostrils. Their boots squelch in inches of buttercream frosting as, rocking back on their heels, they crane their necks upward to regard the point at which cliffs and gulleys of cake meet azure sky. Our heroes have reached the base of the cake pile, Brog Burgest, and consider now the optimal path to the top, where it can only be assumed their quarry lurks.
Carrying on, we get the the top level. Torches burn in purple and orange atop statues of snarling dragons. There's a beholder there, but it doesn't notice us immediately. Checking it's alignment, (LN) Alm. walks forward and talks to it. It gives its name in the course of insisting that we leave immediately unless we wish to be devoured. He doesn't want us to be eaten, because humans give his friend indigestion. Shit. A red dragon, Themberchaud, appears with the full intention of eating everyone and that on which they stand. We fly up into the air to escape, Clementine a pterosaur and louisa doing her usual fly thing. We waited a bit too long, and the dragon is basically upon us. Almuth prays to Eldath, Divine Intervention (nat 20) and boom, dragon no longer eats humans. on a diet. The dragon asks Cogyth to leave us alone, says he no longer eats humans so there's no sense in killing us. "Thumberchaud has finally respected my wishes about... his health?".
There is something like a path, and Almuth does detect traps on it. He finds some, but (Clementine casts "pass without trace", so most of the party is quite sneaky, and we walk up anyway and are not struck. Louisa flies all the way up to the top, stumbling upon an otherworldly scenes that seems to bleed into reality from somewhere else. a keep is surrounded by farms of blue grain and redcap slave masters grave dead-eyed humans in the fields. Louisa sees her form among them. It looks like "______". She spits on a redcap, the one with her body, and is thrown from the mountaintop by an apparition of her curser. Alm. and Got. dance with 4 frosting thralls after she lands, totally unharmed, and gottlob is very graceful, for some reason. Almuth too eventually gets free. The rest of the party snuck past.
Carrying on, we get the the top level. Torches burn in purple and orange atop statues of snarling dragons. There's a beholder there, but it doesn't notice us immediately. Checking it's alignment, (LN) Alm. walks forward and talks to it. It gives its name in the course of insisting that we leave immediately unless we wish to be devoured. He doesn't want us to be eaten, because humans give his friend indigestion. Shit. A fat red dragon, Themberchaud, appears with the full intention of eating everyone along with the cake on which they stand. Almuth prays to Eldath, divine intervention[^divine] and boom, dragon no longer eats humans. on a diet. The dragon asks Cogyth to leave us alone, says he no longer eats humans so there's no sense in killing us. We fly up into the air to escape the cake surface, which the dragon still wants to eat. Clementine is a pterosaur and louisa doing her usual fly thing. The two have a happy reunion. "Themberchaud has finally respected my wishes about... his health?".
{% What happened in part 2? %}
[^ww]: The [Wintershield Watchmen](https://forgottenrealms.fandom.com/wiki/Wintershield_watchmen) is a local volunteer militia that polices the city of Neverwinter and sees to its interests in the surrounding area.
At end of 1, cheshira, freed from her contract to the beholder, raced down the mountain to claim the victims she had trapped in cogyth's name for her own pleasures. As she does so, we intercept and louisa asks her what her problem is. She climbs on a roof and causes a plane shift (or something). The hag summons two eye drakes (did she smoosh them together from a dragon and a beholder? maybe? ask.)
[^ww]: The [Wintershield Watchmen](https://forgottenrealms.fandom.com/wiki/Wintershield_watchmen) is a local volunteer militia that polices the city of Neverwinter and sees to its interests in the surrounds.
[^divine]: A natural 20 at the most opportune time

View file

@ -1,6 +1,6 @@
# Functors and Applicatives, Gratis[^falsehood]
# Derived Implementations
It's usually possible to derive implementations of general structures from those of more specific ones; here, `Applicative` from `Monad` and `Functor` from `Applicative`. This is how to do it, and why not to.
Messing around with OCaml a bit a while ago, I discovered a nice concrete way to show the relationship between functors, applicatives, and monads as they're understood in functional programming. The OCaml functor implementations are basically proof terms for the implications that exist between those classes.
```ocaml
module type Monad = sig
@ -43,58 +43,20 @@ module FunctorOfMonad (M : Monad) :
end
```
Each of these functors accepts an instance of a less general structure and uses only the elements that module provides to implement an instance of the more general structure. `FunctorOfMonad` is boring, being just the composition of the others, but those themselves are somewhat more interesting. Looking at the implementation of `map` in terms of `pure` and `apply`, we can see that we have the function `f` put into an empty context and then applied to `x`. Since we can't know more about the particular applicative functor given, this is the only way to obtain a new instance of its backing type `('a -> 'b) t`, so there aren't any real options here in the way of implementation[^unique-functor]. Still, the meaning in terms of monads and/or effects is worth contemplating; applicatives tell us what application looks like in a context while monads do the same for composition[^makes-sense].
Each of these functors accepts an instance of a less general structure and uses only the elements that module provides to implement an instance of the more general structure. `FunctorOfMonad` is boring, being just the composition of the others, but those themselves are a bit neat. Looking at the implementation of `map` in terms of `pure` and `apply`, you can see that we have the function `f` put into an empty context and then applied to `x`. Since there's no information about the given functor other than that it _is_ a functor, this implementation is the only way to obtain a new instance of the applicative's backing type `('a -> 'b) t`; there aren't any real options here in the way of implementation[^unique-functor]. The meaning in terms of monads and/or effects is still worth contemplating, though: applicatives tell us what application looks like in a context while monads do the same for composition[^makes-sense].
The more involved implementation in `ApplicativeOfMonad` is where we get some options in terms of implementation and also, not unrelatedly, where our problems arise. Because it turns out that there are multiple ways to implement the derivation functor--- also multiple ways to implement a particular monad or applicative--- it becomes hard to predict whether a derived implementation is the expected one without resorting to _ad hoc_ testing, testing that rather defeats the point of "gratis"[^low-effort]. The `apply` function, shown above, is derived from `bind` and `return` by threading `x` and `f` into the context through bind (as `y` and `g`, respectively), thus "stripping off" the context in the body of the lambda, then applying `g` to `y` to obtain a fresh `'b`, and finally passing that `'b` to `M.return`, thus producing a `'b M.t`, which of course is also an `'a t`, since `type 'a t = 'a M.t`[^with-type].
The more involved implementation in `ApplicativeOfMonad` is where we get some options in terms of implementation and also, not unrelatedly, where potential problems crop up. Because it turns out that there are multiple ways to implement `ApplicativeOfMonad`--- also multiple ways to implement a particular monad or applicative--- it becomes hard to predict whether a derived implementation matches the expected implementation without resorting to _ad hoc_ testing, which would sort of defeat the point of deriving implementations (i.e. less work).
To explore concretely how deriving instances can go wrong, consider the following `EitherMonad`.
_A concrete example might serve to illustrate better here, but I haven't been bothered to think of one. Perhaps in the future I'll update this post to go into further detail._
```ocaml
type ('a, 'b) either = A of 'a | B of 'b
We derive `apply` function shown above from `bind` and `return` by threading `x` and `f` into the context through `bind` (as `y` and `g`, respectively), thus "stripping off" the context in the body of the lambda, then applying `g` to `y` to obtain a fresh `'b`, and finally passing that `'b` to `M.return`, thus producing a `'b M.t`, which of course is also an `'a t`, since `type 'a t = 'a M.t`[^with-type].
module EitherMonad (T : sig type t end) : Monad with type 'a t = ('a, T.t) either = struct
return x = [x]
let rec bind (f : 'a -> 'b list) : 'a list -> 'b list = function
| [] -> []
| x :: xs -> f x @ bind f xs
end
```
This has the following derived `Applicative`.
```ocaml
derived app for bar monad
```
And then, here's a lawful[^legis] implementation of `Applicative` for `bar` which is _not_ the one produced by `ApplicativeOfMonad (BarMonad)`.
```ocaml
lawful other bar
```
Just to be sure, let's check that this new instance satisfies the required properties, i.e. that
1. `apply (pure Fun.id) x` = `x`
1. `apply (apply (apply (pure Fun.compose) f) g) x` = `apply f (apply g x)`
1. `apply (pure f) (pure x)` = `pure (f x)`
1. `apply f (pure x)` = `apply (pure (fun g -> g x)) f`
for `BarApplicative'` just as for `ApplicativeOfMonad (BarMonad)`.
{% these are identity, composition, homomorphism, and interchange %}
Monads have their own laws of course, but while a lawful monad is guaranteed to entail a lawful applicative, as we can see, it isn't guaranteed to produce the _desired_ applicative when given as input to a particular derivation functor. The alternate definition above may or may not be a useful one, but the point is that the arbitrary nature of what applicative implementation is considered "correct"[^correct] means you don't `per se` get the applicative you want when deriving an instance; rather, you get the version that accords with the monad, which is _probably_ what you want, but perhaps not. The nice thing about OCaml here, as opposed to Haskell with its typeclasses, is that there's no barrier to having multiple implementation of the `Applicative` signature, so it's easy to implement a different instance without ambiguity. Furthermore, because `ApplicativeOfMonad` operates on a generic applicative and thus cannot make use of any peculiar structure in its argument, even a result that does behave as desired may be less performant in code than a decent handwritten version--- compare such implementations for *FooMonad*.
```ocaml
foo monad stuff
```
Monads have their own laws of course, but while a lawful monad is guaranteed to entail a lawful applicative--- as we can see by the signatures of our functors--- the functors aren't guaranteed to produce the _desired_ applicative for a particular input. The arbitrary nature of what applicative implementation is considered "correct"[^correct] means you don't _per se_ get the applicative you want when deriving an instance; rather, you get the version that accords with the monad, which is _probably_ what you want, but perhaps not. Furthermore, because `ApplicativeOfMonad` operates on a generic applicative and thus cannot make use of any peculiar structure in its argument, even a result that behaves "correctly" is likely to have worse performance than a decent handwritten version.
[^with-type]: In regards to the `with type = ...` syntax, there is a subtlety of the OCaml module system that if a module is defined with a particular `module type` (a.k.a. signature) attached--- for example, `module M : S = struct ...`--- all the types that are abstract in the signature `S` will _also_ be abstract in the module `M` itself. This means that the compiler can't see or be convinced that for some `F (M)` with `type t = M.t` in `F`, `M.t` and `(F (M)).t` are equal, because both types are abstract, i.e. the underlying type is not available. To fix this, we can explicitly expose the equality by using the `with type` construct. In the above example, `Functor with type 'a t = 'a M.t` exposes the equality of `'a t` and `'a M.t`, so that functions defined as expecting arguments of `'a t` can accept `'a M.t`, and _vice versa_, etc.
[^falsehood]: Unsurprisingly, that's a lie. Isn't it always? You have to buy a monad first.
[^1ml]: See [1ML](https://people.mpi-sws.org/~rossberg/1ml/1ml-jfp-draft.pdf) for an OCaml-like language without this stratification.
[^pipe-last]: This idea is that to harmonize with OCaml's automatic currying, parameters to which a function is more likely to be "partially applied" should be earlier in its parameter list. This cuts down on syntactic noise; particularly, pipes which apply to the _last_ argument (see?) don't require shuffling-about of the parameter order, e.g. `xs |> List.map f` rather than `xs |> (fun xs -> List.map xs f)`. Jane Street will tell you that [labels](https://ocaml.org/docs/labels) can [address](https://opensource.janestreet.com/base/) the issue best, but to my eyes, `:` and `~` were never meant to spend so much time that close to one another.
[^makes-sense]: It makes sense then, that having a monad nets you an applicative functor--- how can one talk about composition without having application first, which is needed to consider function-like objects at all?

View file

@ -1,6 +1,6 @@
# Umbral Gaze ½
It's early morning; our intrepid adventurers are just beginning their day. They awaken in Venron, a small inland village equilatitudinal to the southern Sword Coast, where, for one reason or another, each wound up stopping for rest the night before.
It's early morning; our intrepid adventurers are just beginning their day. They awake in Venron, a small inland village equilatitudinal to the southern Sword Coast, where, for one reason or another, each wound up stopping for rest the night before.
The centaur Clementine[^clementine] helps a farmer store his crops, managing bins of grain with ease. On her back hangs a powerful bow, but she has no need of it now--- as she takes in the wind and the smell of earth and the stirring sounds of nature, she's at peace.