ohua-dev / ohua-core

Core Haskell library for the compiler
https://ohua-dev.github.io
Eclipse Public License 1.0
5 stars 0 forks source link

Proposal for incorporating explicit state intialization and association #22

Closed JustusAdam closed 5 years ago

JustusAdam commented 5 years ago

Problem description and solution requirements

The Rust backend for Ohua requires an explicit association between the state of a stateful function and the function itself. As a relic from Java this has up to this point been implicit (as a class) or linked using a backend mechanism such as clojure metadata.

This implicit approach is unfeasible in Rust, because the implicit association of a class is not present, and more importantly the reflection mechanisms for discovering initializers for state do not exist.

Furthermore we believe that an explicit state association will provide more flexibility for the user and may be used to implement state sharing stateful functions.

Requirements

Additionally our proposals so far are also designed such that they may be used in the future to express state sharing. While this is not a necessary property to solve the issue at hand, it is a desirable property.

Solution variants

We believe that new syntax may be necessary to implement state initialization and association in a user friendly way.

Three variants were discussed so far.

Arrow assignment with implicit association

let s <- initState in
  statefulFunction s a b c

In arrow syntax a special new assignment operator <- (or similar) is used to explicitly initialize a state cell. This binding (s in the example) can only be used as a first argument to a stateful function and implicitly associates as the statefulFunction's state.

Advantages

Disadvantages

with operator (explicit initializer association)

(statefulFunction with initState) a b c

let boundSf = statefulFunction with initState in
boundSf a b (boundSf c)

let (sharingSf1, sharingSf2) = function1, function2 with initState in
function1 a (function2 b c)

The with operator associates a state initializer with a function. with is a binary operator and its rhs must be a callable expression in the target language1. State sharing is done by either reusing an already bound function (boundSf) or initializing multiple stateful functions with the same function (function1 and function2).

1: The precise nature of the RHS is variable. We can use a function or maybe even expression, which would allow for dynamically created state.

Advantages

Disadvantages

Regular let with explicit state association

let s = initState in
(statefulFunction on s) a b c

let s = initState in
let boundSf = statefulFunction on s in
statefulFunction s b (statefulFunction c)

This variant is a blend of <- and with. It explicitly associates a state cell with a stateful function using on. However unlike with it allows binding the state cell earlier using let which enables state sharing by using the bound state with multiple functions or using an on bound function multiple times.

Depending on the availability of dynamic state binding and scoped state certain restrictions may be placed on what the RHS of on has to be. For instance, in the beginning we require that it resolve to an env binding.

Advantages

Disadvantages

On new syntax

I think it is worth mentioning that we may not necessarily require actual new syntax for this feature. A similar effect may also be achieved by using a function (such as init) that is especially recognized by the compiler.

statefulFunction (init initState) a b c

let s = init initState in
statefulFunction s a b (statefulFunction s c)

Similar as with <- init values would be traced through the code and implicitly become the state of a function to which they are the first argument.

The same advantages and disadvantages as with <- apply.

One advantage of this approach over all the others is that no new syntax needs to be introduced. This means that handling init can be entirely done in the core library with no change to any of the parsers. As a disadvantage, there can not be a stateful function named init, similar to the new keyword in languages like Java.

sertel commented 5 years ago

Thanks @JustusAdam for putting together this proposal.

This one is very important, so we should understand what that is conceptually. Here is my take on it:

I see this as an introduction of a special type into our language. One can view it like this: There exist two (higher-kinded) types in our system

More formally, "stick" means that this function actually turns a function f:: a -> b into f:: (a,s) -> (b,s). I say stick because semantically, we/Ohua executes these functions for example inside an smap by reusing the new state for the next invocation, i.e., the state sticks with the function (for its lifetime or until it is reassigned). In Haskell, such functions of the Kleisli category are represented in terms of the State monad and the according Ohua-like execution is achieved via the concept of a value recursion using mfix. This is also what we do: We essentially fold over the state.

I like the discussion above about making the state aspect obvious to the programmer in two places:

But after all, we are talking about a type-system aspect here, so if the programmer gets guided by the compiler then making it only obvious during creation is fine for me. At the moment, I do favor the on-variant. But whatever syntactic sugar we decide upon, in the end this should desugar into a normal let in the typed lambda caluclus of ALang. (See issue #23 for my proposal to have a language for this syntatic sugar to keep ALang clean.)

JustusAdam commented 5 years ago

A short note on how I implemented this:

I am currently in the process of implementing this both in the compiler and the parsers. I decided to go with an operator named with but with the semantics of on. The reason is because I think with, as a word, makes more sense.

sertel commented 5 years ago

I have the basic implementation in place. We will follow up aspects such as shared state etc. in other issues once we get there.