gren-lang / gren-lang/compiler
Transparent tasks and commands
Nobody has claimed this yet.
- Dominant language
- Haskell
- Stars
- 503
- Forks
- 29
- PR merge metrics
- No merged PRs in 30d
Description
Since Gren is a pure, functional language, it lends itself well to unit and integration testing. Opaque tasks and commands, however, stands in the way of this. It's impossible to inspect, mock or otherwise override tasks and commands in a reliable way.
In Elm, the elm-program-test framework gets around this by having the user define a application specific Effect type. This type is then converted to a Cmd at the last possible time. This is a lot of work, though, and wouldn't have been necessary if tasks and commands weren't opaque to begin with.
This proposal aims to make tasks and commands transparent, to further improve the testability of Gren code.
This proposal depends on the implementation of proposal #218 (tagged values).
Re-defining Tasks as a tagged union
Tasks, especially those that perform side-effects, are often created in kernel code. However, in order to maintain purity they are never executed until it escapes application code and enters the runtime.
So why not represent them as data?
module Task exposing (Task)
import Time
type Task err ok =
| Succeed ok
| Fail err
| Sequence (Array (Task err ok))
| RetrieveCurrentTime { onSuccess : Time.Now -> ok }
...
Each platform package (gren-lang/browser, gren-lang/node) would then define their own Task type, which will be a superset of the core Task type.
module Task exposing (Task)
import Task as CoreTask from "gren-lang/core"
import Bytes exposing (Bytes)
import FileSystem.Permission as FS
type Task err ok =
| ...CoreTask.Task
| FileSystem_ReadFile
{ permission : FS.Permission
, toError : { code : Int, message : String } -> err
, toOk : Bytes -> ok
}
| FileSystem_WriteFile
{ permission : FS.Permission
, content : Bytes
, toError : { code : Int, message : String } -> err
, toOk : {} -> ok
}
Note that, since gren-lang/core's Task is a subset of the platform Task, it's a valid value wherever the platform type is expected.
Cmd
Cmd is really just an opaque type alias for Task Never a. One might ask if we really need a separate type for this, but answering that question is out of scope for this proposal. What will have to change, however, is the opaque nature of Cmd.
When the application's update function return a Cmd, the platform's entrypoint will need to evaluate the underlying Task in order to perform the intended side-effect.
Since the Cmd/Task is transparent, it's entirely possible for the application to define its own Task type which is a superset of the platform's Task. It's also possible for the application, or a framework that's similar to elm-program-test to perform its own evaluation of Tasks. This gives the user the flexibility to ignore, mock or transform specific side-effects.
A big benefit of this design is that the user has a much easier way of testing their applications, with minimal changes required in their existing code.
This will require a lot of work from the core team, but it might actually end up moving more code from JS to Gren, which is a big win.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading proposal #218 on tagged values, then trace the core and platform definitions of Task and Cmd and the platform entrypoints that evaluate them. Done means tasks and commands are transparent data, platform-specific task types can extend the core representation, and applications or testing frameworks can inspect, mock, or transform effects before runtime evaluation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100