How would one use phantom types in Roc to translate the following Elm example to Roc? https://sporto.github.io/elm-patterns/advanced/flow-phantom-types.html
Phantom types are supported if you use an Opaque type https://www.roc-lang.org/tutorial#opaque-types
I'm trying to think of a good example to point to, but haven't got anything yet.
I thought we had one on https://www.roc-lang.org/examples
But it doesn't look like it. I can make a PR to add on soon.
I did try this
Order : {price : Str}
Step step : Step Order
Start : [Start]
OrderWithTotal : [OrderWithTotal ]
OrderWithQuantity : [OrderWithQuantity]
Done : [Done]
start : Order -> Step Start
setTotal : Int -> Step Start -> Step OrderWithTotal
adjustQuantityFromTotal : Step OrderWithTotal -> Step Done
setQuantity : Int -> Step Start -> Step OrderWithQuantity
adjustTotalFromQuantity : Step OrderWithQuantity -> Step Done
done : Step Done -> Order
However I do get the error
── SYNTAX PROBLEM in main.roc ──────────────────────────────────────────────────
I got stuck here:
68│ setTotal : Int -> Step Start -> Step OrderWithTotal
^
Whatever I am running into is confusing me a lot! Normally I can give
fairly specific hints, but something is really tripping me up this
time.
However its only setTotal and setQuantity that give error the other functions do type check.
Here's an example I used recently https://github.com/lukewilliamboswell/roc-experiment-js-dom/blob/0842577df83efa53a2d2584a7b38d4fb067f8f20/platform/Html.roc#L14
Here the type variable msg
isn't used, it's phantom
I use plenty of Phantom types in Weaver: https://github.com/smores56/weaver/blob/3881ac2d6aeb20150e7473ab2b81b1beaf9f219c/package/SubCmd.roc#L112
The CliBuilder opaque type is:
CliBuilder data fromAction toAction := {
parser : ArgParser data,
options : List OptionConfig,
parameters : List ParameterConfig,
subcommands : Dict Str SubcommandConfig,
}
fromAction
is the current state of the builder, and toAction
is a possible future state
Is there like a canonical example people use for these? Something simple?
I like the idea of also showing it used with record builder for states.
In the first linked example in SubCmd
, you can see that the fromAction
is GetOptionsAction
and the toAction
is GetParamsAction
. This ensures that you can only add subcommands to CliBuilder
s that have defined options so far, but if you defined subcommands or parameters, it won't typecheck
@Luke Boswell I'm not sure. The weaver usage is a little complex for a good teaching moment. When the tutorial rewrite happens we'll want a simpler example
I'd say the general approach of state tracking for builders is the general way I see them used
Is there some ETA for the tutorial rewrite or will it happen when it happens ;)
Its this we are talking about right? https://www.roc-lang.org/tutorial
Henrik Larsson said:
Is there some ETA for the tutorial rewrite or will it happen when it happens ;)
I don't think anyone currently owns it. @JanCVanB just recently added some stuff, but it's still in the form of a single, giant page. This has the benefit of implying that Roc is a small language that you can learn quickly, but it's still unwieldy in my opinion. @Richard Feldman I think was wanting to do it himself, but maybe I'm mistaken.
I think Roc would be very well served with a "tourtorial" like Gleam has, but even a mdbook-like tutorial would help digestability IMO
I think if someone felt strongly about teaching and wanted to update the tutorial, we wouldn't complain about them taking up the sword of progress
yeah I originally wanted to delay personally doing a tutorial rewrite until purity inference was out, and now I want to delay it until static dispatch and custom types have landed :big_smile:
so it'll be months, and any incremental improvements in the meantime would be both great and also would become part of the inputs to the rewrite!
Incremental updates being changes to the content of the current layout, but not any structural changes.
yeah
Richard Feldman said:
yeah I originally wanted to delay personally doing a tutorial rewrite until purity inference was out, and now I want to delay it until static dispatch and custom types have landed :big_smile:
This is always my problem with docs
Always something to delay for
usually I wouldn't bother if it's just small changes, but both static dispatch and purity inference are like "rewrite most to all of the examples" changes and custom types affects a large chunk too :sweat_smile:
I thought you had a larger pivot in mind. Like you've mentioned how you wanted to approach the introduction for people from another angle or something.
yeah I do :+1:
oh yeah which reminds me, for
loops too :sweat_smile:
Thanks for all the input, just for history on OP, the Roc way for state machines with phantom types based on the Elm example looks like this I think.
Order : {price : Str}
Step step := [Step Order]
Start : [Start]
OrderWithTotal : [OrderWithTotal]
OrderWithQuantity : [OrderWithQuantity]
Done : [Done]
start : Order -> Step Start
setTotal : Int *, Step Start -> Step OrderWithTotal
adjustQuantityFromTotal : Step OrderWithTotal -> Step Done
setQuantity : Int *, Step Start -> Step OrderWithQuantity
adjustTotalFromQuantity : Step OrderWithQuantity -> Step Done
done : Step Done -> Order
Last updated: Jul 06 2025 at 12:14 UTC