Ideas for changes to Wipple syntax
I have a few ideas for cleaning up Wipple's syntax around generics, custom error messages, and assets without changing the surface level of the language.
Wipple
I have a few ideas for cleaning up Wipple’s syntax without changing the surface level of the language. I think these changes will help improve discoverability and the editing experience across the Wipple Playground and local IDEs.
Generics
Currently, Wipple uses an arrow to separate the definition of type parameters from their use.
However, declaring the type parameters and bounds first means that the actual type of a function can
be buried, especially if the signature is complex. For example, the zip
function in Wipple
currently looks like this…
zip :: Left (infer Left-Element) Right (infer Right-Element)
where (As-Sequence Left Left-Element) (As-Sequence Right Right-Element) =>
Left Right -> Sequence (Left-Element ; Right-Element)
The first 70% of the signature involves introducing type parameters and bounds, while the actual
purpose of zip
— merging two sequences — isn’t apparent until the last 30%. In general, it’s
difficult to quickly browse functions within the standard library documentation because of all this
type information.
My idea is to reverse the order of generics, placing bounds after the real type. This means type
parameters will have to be implicitly defined, like in Haskell, and must be lowercase to prevent
ambiguity with existing types. With that change, zip
looks like this:
zip :: left right -> Sequence (left-element ; right-element)
where (infer left-element) (infer right-element) (As-Sequence left left-element) (As-Sequence right right-element)
I think that reads much better!
(Also note that modifiers like infer
and type defaults are moved to after the where
keyword.)
Custom error messages
Custom error messages are implemented in Wipple using the special Error
trait, which accepts a
compile-time string as input. This has added a lot of complexity to the type system and visual
noise to code involving custom error messages:
instance (Mismatch Times ({body} -> Unit))
where (Error ("missing `repeat` here" ; Error-Fix "add `repeat`" ("`repeat _`" Source)))
Another issue with the design of Error
is that it isn’t consistently enforced across the language.
When defining an instance for a trait, Wipple requires that you provide a value if and only if the
trait defines one. Wipple doesn’t look at the bounds (including Error
) to determine whether the
instance’s value will ever actually be used. This means that when you create an instance for a
custom error message, you have to put a placeholder like ...
or unreachable
, which isn’t ideal:
default instance (Describe value) where (Error ("`_` has no description" value)) :
unreachable
Even more confusingly, you can put any valid expression in place of unreachable
and Wipple will
accept it, even though the code will never be executed!
To improve this, I want to promote “error instances” to be part of the language. Error instances are
written with the error
keyword, and are written without a value (regardless of whether the trait
requires one or not). Rather than providing a string within the type signature, Wipple will read the
instance’s documentation comment:
-- [`value`] has no description.
default error instance (Describe value)
The “code link” syntax for rendering types is borrowed from Rust.
Including documentation comments within error messages is part of a larger effort to make the analysis behind Wipple’s diagnostics visible to users. I’m excited to share more in a future post!
Assets
In the Wipple Playground, expressions surrounded in square brackets ([ ]
), known as assets, are
rendered as interactive widgets. The editor has to scan through the code to find these assets before
rendering them, and I’ve noticed this process can be especially slow on lower-performance
Chromebooks — students are seeing “flashes” of code meant to be internal, such as color hex codes or
music data, and experience sluggish typing. When the assets don’t render properly at all, the code
becomes unusable.
Instead of having all the information for constructing an asset inline, I want to put it in a metadata section. This would be hidden by default in the Wipple Playground, but locally, it would be stored in a JSON frontmatter section like so:
---
{
"runtime": "turtle",
"assets": {
"a": {
"type": "Color",
"value": "blue"
},
"b": {
"type": "dropdown",
"selection": "50",
"options": ["10", "20", "30", "40", "50", "100", "200", "500"]
}
}
}
---
color [a]
forward ([b] pixels)
Assets are resolved during parsing: The built-in dropdown
asset is replaced with its selection
(parsed as an expression), and custom assets are replaced with an opaque JSON object that has the
defined type
. This design means dropdowns can support options of different types! It’s also open
to future extension and more amenable to changes in format — on the playground, I can store metadata
separately from code and more easily modify the schema of specific assets.
Conclusion
These changes to Wipple’s syntax will likely go completely unnoticed by most, and that’s a good thing; it means Wipple is becoming a more mature language! I don’t have a timeframe on implementing these changes into the current version of Wipple — I have a few other, more large-scale ideas I want to explore first and make sure are compatible with the ideas here.