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.

Code with assets becomes unusable when the assets fail to render.

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.