Skip to content

Open Questions

kevin-montrose edited this page Apr 10, 2021 · 2 revisions

Open Questions

Introduction

A number of pieces in Cesil's design are still "up for debate" while it is pre-1.0. Some, and perhaps all, of these will eventually be discussed at length in a blog series. For now, this document serves as a repository for future reference.

Open Questions

For configuration purposes Cesil uses the TypeInfo class in preference of the Type class. TypeInfo is newer, and lacks a number of historical "mistakes" of Type like array returns.

That said, TypeInfo requires extra calisthenics to use.

The open question is: Should Cesil interfaces use TypeInfo, Type, or something else (perhaps both)?

"Static" Delegate Naming

For the wrapper types Getter, Reset, Setter, and ShouldSerialize Cesil defines multiple delegates that could back them, one that takes a row type and one that is "static" and takes no row type.

Cesil currently gives these "static" delegates a different name, StaticGetterDelegate<TValue> vs GetterDelegate<TRow, TValue> for example. Another alternative would be to use overrides, and simply vary the number of parameters the delegate takes but keep the names the same.

The open question is: Should Cesil distinguish delegates purely on parameter counts, or should it stick to using different names?

Chaining Of Getter, Reset, Setter, and ShouldSerialize

The wrapper types Getter, Reset, Setter, and ShouldSerialize do not implement the Else(...) pattern that other wrappers (DynamicRowConverter, Formatter, InstanceProvider, and Parser) do.

This is a consequence of all those concepts being inherited from "proper" .NET where failure is not considered.

While it is possible for consumers to construct their own chaining for these wrappers, Cesil could also offer this "out of the box" at the cost of some additional conceptual (and code, especially in the cases of ShouldSerialize and Reset) complexity.

The open question is: Should all wrapper types in Cesil support the Else(...) pattern?

Wrappers With GetDefault(...)

Parser and Formatter wrappers have GetDefault(...) methods which return the "reasonable and expected" default for a given type - this happens coincides with what the Default Type Describer uses for parsing and formatting.

There's no particular reason that the equivalent GetDefault(...) methods couldn't be added to other wrappers, where the contract would be rough "whatever the Default Type Describer does for this member/type/etc." That said, expected default behavior will probably be less clear to consumers for things other than parsing and formatting.

The open question is: Should all wrapper types in Cesil have a GetDefault(...) method?

Missing Supported Types

Cesil has built in support for parsing and formatting:

The open question is: Are there any other types that Cesil should support by default?

Missing "Stream" Types

Cesil supports reading from:

Cesil supports writing to:

The open question is: Are there any other "stream" types that Cesil should support reading from or writing to?

Nullable Reference Treatment

Cesil will deserialize null into a C# 8 non-nullable reference type, as non-nullablility is a compile-time construct - the actual runtime value is perfectly nullable. Put another way, while Cesil itself carries null annotations it's runtime behavior is nullability agnostic.

Cesil could detect nullablility annotations and change it's behavior to match, though this puts consumers in an odd spot if the consumer has not enabled nullable reference types but the type being read (or written) is from a package with annotations. Concretely, Cesil could fail to set a member to null when the consumer writing the same null assignment would succeed - which would be quite odd behavior.

On the other hand, Cesil's current behavior makes it very easy to subvert non-null reference type checks.

This discussion is complicated by the fact that, at time of writing in 2020, most C# code is not nullable reference aware. It seems reasonable to expect that Cesil will regularly be used in mix-nullable code bases.

The open question is: Should Cesil inspect nullable annotations and change it's default behavior to prevent null from being assigned to members bearing non-null annotations?

Asynchronous Wrappers

Cesil's has a number of wrapper types that abstract various parts of read and write operations.

All of these wrappers can only be backed by synchronous operations, ie. methods or delegates that block until completion. While this it is obviously necessary to support synchronous operations, Cesil lacks any support for asynchronous Getters, Formatters, InstanceProviders, Parsers, Resets, Setters, and ShouldSerializes.

Adding support for asynchronous wrappers would increase configuration complexity, but that would probably be manageable. A bigger question would be how to handle the combination of asynchronous wrappers and synchronous reading and writing. In all likelihood this would lead to cases where some kinds of Options could only be used with async readers or writers.

There are three open questions:

  • Should Cesil allow wrappers to be backed by asynchronous delegates and methods?
  • If so, how should asynchronous wrappers in a synchronous context behave?
  • Finally, what sorts of return types should asynchronous wrappers support (that is, just ValueTask or maybe anything with a GetAwaiter() method)?

Missing Extension Points

Cesil gives consumers ways to make decisions when readers and writers are created, when values are fetched, when values are written, when rows are created, when values are set on rows, before values are set on rows, when dynamic rows are converted, and when dynamic cells are converted. Cesil also exposes lots of Contexts during these operations, and allows for a consumer provided value to be passed alongside these contexts.

The open question is: Are there any extension points that are missing that Cesil should add?