Open Janiczek opened 5 years ago
You might want to specify a bit the constraints. For example you could do mathematical operations in pure Elm, if you don't care that they will be horrifically inefficient. But for any practical purpose you'd want the Kernel version. On the other hand for some of the data structures a pure Elm solution may be less of a trade off.
For the mathematical operations, I don't expect us to implement Peano arithmetic or something. We can keep the type Int = Int
and overwrite stuff in the compiler, like the official compiler does.
I gues this is more of a "go through the kernel modules" than "go through the whole elm/core" type of ticket. So, I'm editing the original description to say that.
I'm not sure how more to make this ticket clearer. We can definitely talk about specific cases as they come up. (Either in a PR / this issue / on Discord)
Given that Core's Kernel can be imported from other libs (https://github.com/elm/browser/blob/master/src/Elm/Kernel/Browser.js), it means that we would have to completely split off from Elm packages.
Ideally we'd allow usage of the packages (download them and all) but have our overrides for each platform (as much Elm-only as possible to minimize the amount of non-Elm overrides needed per platform)
Link to some further discussion on Discord: https://ptb.discordapp.com/channels/578305644780716039/578305645824966669/602182350352285717
Core module | Non-kernel | Notes |
---|---|---|
Platform.Sub | ? | |
Platform.Cmd | ? | |
Array | Slow | Uses JsArray to be fast - needs more investigation. |
Basics | Impossible and slow | Particularly tricky to get right is (==) , slow comes from Bool s. |
Bitwise | Impossible | |
Char | Impossible | We need toUpper , toLower , toLocaleUpper , toLocaleLower , toCode and fromCode . |
Debug | Impossible | |
Dict | Already | elm/core uses a custom type based red/black tree implementation. |
List | Possible | The must be some subtulies here - possible relating to the [] syntax. |
Maybe | Already | |
Platform | ? | |
Process | ? | |
Result | Already | |
Set | Already | Just uses Dict s. |
String | Slow | An implement dertermined to avoid kernel code could use type String = List Char . |
Task | I doubt it | Calls out to Elm.Kernel.Scheduler . |
Tuple | Already | Very simple module. |
Array
, Set
and Dict
must be special cased by (==)
.Concerning Basics: its doable for at least four Functions:
(&&) : Bool -> Bool -> Bool
(&&) a b =
case (a, b) of
(True, True) -> True
(_, _) -> False
and similar for (||), not, xor.
This would make Bitwise also possible if we allow multiplication and division. I wouldn't say it's impossible but very slow. Technically, a lot of the "impossible" stuff could be implemented using Church Encoding. It's not nice but a fallback ... for languages that don't implement integers or addition?
Concerning toUpper/toLower -> This could also be a huge switch case. Not beautiful but possible.
Process is as doable as Task. Both require access to a scheduler.
Platform.Sub and Platform.Cmd depend both on Platform. Apart from Ports and the Main Loop feasible but hard. There is an omega combinator in the untyped lambda calculus, that could handle endless loops, but I dont understand it completely.
@j340m3 Doesn't making &&
a normal function mean that you need to evaluate both arguments before passing them into that function? ie. shortcircuiting would not be there anymore?
@Janiczek My apologies. You're absolutely right, I overlooked that detail. In this implementation, I really doubt this would be shortciruiting. I'll experiment if shortcircuiting can be implemented in similar fashion to the Maybe-Monad.
The other idea I would propose is the usage of _:
(&&) : Bool -> Bool -> Bool
(&&) a =
case a of
False -> \_ -> False
True -> id
Does anyone know if this would shortcircuit? The documentation says AFAIK "discards the input". Is the elm/compiler optimizing it then as shortcircuit?
Parser of Wildcard
Wildcard -> AST.Source.PAnything -> AST.Canonical.PAnything
The most notable difference in handling wildcards vs. variables is IMO here but I have to admit that I'm a bit clueless of the behaviour/role of the destruct mechanism. HerePVar
andPAnything
are used exactly the same way. Here they are handled differently but I believe this is not relevant for the execution.
type Bool = Bool BVal
type BVal
= True
| False
true : Bool
true = Bool True
false : Bool
false = Bool False
(&&) : Bool -> Bool -> Bool
(&&) (Bool a) =
case a of
False -> \_ -> Bool False
True -> id
Would shortcircuit certainly because the second argument is never destructured. It just means that everywhere a Bool has to be implemented, we will have to plug the "monad" inbetween (aka Box it)
We can check the shortcircuiting with Debug.log:
Unfortunately Ellie won't allow me to swap a different && instead of the basics one, and during work hours I don't have the time to go hacking elm/core on my machine :) In any case, functions won't ever short circuit:
so our only hope is that binary operators have the ability to short circuit built-in.
I'll check this later with a custom binop :)
Perhaps the question to ask is: how important is the fact of short-circuiting? I don't think in the context of Elm it makes much of a semantic difference; it has only two observable effects: performance will be different as well as Debug.log
.
I don't think that the performance difference would even be very significant in most cases.
(EDITED after comments from Jakub)
Ideally we'd like to create an
elm-in-elm/core
package that does everythingelm/core
does but in pure Elm (without kernel modules). That won't be entirely possible but we can try and minimize the amount of "magic" Elm needs the underlying platform to support.Go through all the kernel modules in
elm/core
and create a table, say innotes/elm-core.md
orxls
or whatever, documenting which of those can be done in pure Elm. ("Don't know" is OK too!)In case of any doubts, ask here / in your PR / on Discord!