As a way to convince yourself that the type of runBuilder
is safe (or, at least as safe as the traditional API), you could, instead, have given yourself
newBuilder :: (Builder %1 -> Ur a) %1 -> Ur a
runBuilder' :: Builder %1 -> Ur Text
The you could have defined runBuilder
as follows:
runBuilder :: (Builder %1 -> Builder) -> Text
runBuilder f = unur $ newBuilder build
where
build :: Builder %1 -> Ur Text
build = runBuilder' . f
I’ve had a quick look at the implementation, it has a lot of unsafeCoerce
-s. It’s difficult to estimate the cost of these (though it’s definitely not trivial because unsafeCoerce
between non-linear and linear functions prevent some inlining optimisations currently).
It would be worth considering defining Builder
as
data Builder where
Builder :: Text -> Builder
(notice the non-linear arrow)
This would mean one extra box everywhere (I have to admit that I haven’t yet gotten around to do the worker-wrapper split for unrestricted constructors like this; however, inlining should still remove a bunch of the boxes), in exchange of avoiding a lot of unsafeCoerce
-s.
I honestly don’t know which is faster.