-
Notifications
You must be signed in to change notification settings - Fork 0
/
NEWS
133 lines (104 loc) · 6.33 KB
/
NEWS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# NEWS
A little inline blog documenting the development of this library.
## 2020/5/4 -- Subscription mental models
I have consistantly found subscriptions to be the most confusing thing about
elm. The best way to grok them is just to use them and not think too much. I
think the cannonical explanations of commands live in the [effects/time] and
the [interop/ports] sections of the elm guide. Both of these sections are very
useful for helping folk understand how to effectively _use_ a subscription but
give nothing away about what a subscription really is.
Here is my take (and I guess, as of today, another-elm's take) as to what a
subscription is. Here is the mental model in three steps:
1. Events happen in the world, the elm app cannot control events, cause events
or prevent events from happening. They just happen.
2. The elm runtime detects all events always: nothing happens that the runtime
does not know about.
3. The runtime uses the `subscription: model -> Sub msg` function given to it
on application init to filter events. If one of subscriptions currently
active matches an event detected, the elm runtime will use that subscription
to process the event and send it to the app (via the `update: msg -> model
-> (model, Cmd msg)` function).
(A subscriptions returned by `subscription: model -> Sub msg` is active
between the time that the function returns and the next invocation of the
function.)
To take the example of the `Time.every` function:
1. A event occurs repeatedly at a very small time period, say one millisecond.
2. The elm runtime detects all these notifications.
3. The runtime checks for a `Time.every` subscription, **if** it has one
**and** the current time (measured relative to some epoch) is exactly a
multiple of the time interval associated with the subscription **then** the
runtime uses the tagger associated with the subscription to send a message
to the app.
There are three issues with the mental model applied to the `Time.every`
function:
1. **It would require a huge amount of overhead in the runtime.** We can write
clever kernel code that matches the mental model without actually
implementing it.
2. **Elm uses floating point interval durations** which makes talk of 'exact
multiples of intervals' invalid. I think using floating point interval
durations is wrong. Time calculations should be exact. Therefore, I say we
can ignore this issue for now.
3. **This mental model does not match behaviour of offical elm/time!** Oh well,
at least it matches another-elm implementation.
On my todo list is to write a proposal to better specify the behaviour of
`Time.every` and to remove all the floating point badness from the module. Not
very high up my todo list though, partly because no one ever pays any attention
to such proposals.
Anyway, I have been rambling a bit, time(?) to bring this back on track and to
conclude. Subscriptions are hard to understand and this is not helped by the
lack of an officially blessed mental module. Implementing subscriptions in the
elm runtime is impossible with some mental module to base the implementation
on. Until such time as I find a better mental model, I will use this model for
subscriptions in another-elm.
[effects/time]: https://guide.elm-lang.org/effects/time.html#subscriptions
[/interop/ports]: https://guide.elm-lang.org/interop/ports.html#incoming-messages-sub
## 2020/5/2 -- Another elm
Here goes, a proper home for my take on the elm/* libraries. The idea is one
repository (<https://github.com/another-elm/std)> containing everything you
need (except the compiler) to use my custom implementation of the core
libraries.
The big new useful thing is a script './tools/another-elm' that can be used as
a drop in replacement for the elm compiler. It is (currently) pretty janky, be
prepared to regularly run `rm $ELM_HOME/another -r` when the compiler gets its
thread blocked indefinitely in MVar operators. That the elm compiler is
insanely fast means that running the elm compiler two or three times extra each
time is very doable. Try it out:
sudo mkdir -p /opt/elm/
sudo chown $USER /opt/elm
git clone <https://github.com/another-elm/std> /opt/elm/std
sudo ln -s /opt/elm/std/tools/another-elm /usr/local/bin/another-elm
## 2020/4/26 -- Merge elm/random back into core
In my book, things that can manage effects belong in elm/core. This merge
required replacing effect manager code with an alternative (in this case
channel based) implementation.
The new implementation is not that nice. Maybe it will look better when I
manage to unifiy the two Task types. I think this is a case where the effect
manager abstraction really worked well. I might just have my rose tinted
specticles on.
## 2020/4/26 -- A new internal module `Platform.Raw.Impure`
This module contains an abstaction for functions that **do things** when
they are run. The functions in this module are constrained to take one argument
and return the unit tuple.
Why can we not use Task's for this, given that this is _exactly_ what they are
intended for. Well, two reasons
1. Sometimes we need a guarantee that the function will be run exactly when we
need to run. Task are always enqueued; they are only run after stepping
through all the previous Tasks in the queue. Sometimes, this is not
acceptable, for instance when updating the listeners for a subscription
effect.
2. We need to use impure functions to run Tasks. The
`Platform.Raw.Scheduler.enqueue` function takes a Task, adds it to the
scheduler queue and, if the scheduler is not currently stepping tasks (i.e.
this is not a reentrant call to `Platform.Raw.Scheduler.enqueue`), starts
stepping. This function is impure. However, if we represented it as a Task
we would have an infinite loop!
Hopefully, use of this module can be reduced to a couple of key places and
maybe even inlined into the scheduler is that is the only place that uses it.
Hopefully, it will help us move all effectful functions out of elm.
## 2020/04/26 - the future (?)
I wan't to move to away from callbacks and towards async/await and promises (or
futures). Firstly, I find that async/await is much easier to reason about than
callbacks and leads to much prettier code. Also, in the back of my mind is the
desire to eventually port the core libraries to rust for native compiled elm
code.
Todays change is just cosmetic, but hopefully is step 1.