Let's say we have the following data:
hobby :: { name :: String }
hobby = { name: "Football" }
Before visible type applications we could write this:
newHobby = Lens.set (prop (Proxy :: Proxy "name")) "PureScript" hobby
Now we can write this:
newHobby = Lens.set (propVTA @"name") "PureScript" hobby
Another example is with variants:
myVariant = inj (Proxy :: Proxy "variantName") "variantValue"
Becomes
myVariant = injVTA @"variantName" "variantValue"
An easy way to write propVTA
as kind of an adapter is the following:
propVTA :: forall @a r. Lens' r a
propVTA = prop (Proxy :: Proxy a)
The interesting and new bit is the @
sign before the a
which allows
people calling our propVTA
function to specify a concrete (monomorphic)
type for the general a
type variable as we did in propVTA @"name"
.
The simplest example of a function that takes a VTA is the following:
vtaIdentity :: forall @a. a -> a
vtaIdentity v = v
At the call-site would then be vtaIdentity @Int 5
or vtaIdentity @Boolean false
, etc.
When you have multiple @
signs in the forall
bit of your function definition
their order matters. We will demonstrate this with the help of a function that
renames a record field:
Example:
x = { boringName: "value" }
y = renameVTA @"boringName" @"excitingName" x
renameVTA :: forall @oldName @newName rec1 rec2.
IsSymbol oldName =>
IsSymbol newName =>
-- Some constraints elided for simplicity
{|rec1} ->
{|rec2}
renameVTA = unsafeRename
(reflectSymbol :: (Proxy :: Proxy oldName))
(reflectSymbol :: (Proxy :: Proxy newName))
In some cases you will still need to require Proxy
s in your APIs.
But at least now you can define a helper function
prx :: forall @a. Proxy a
prx = Proxy
And then write
foo = myFn (prx @"Help")
Or even
applyProxy :: forall @p a. (Proxy p -> a -> b) -> a -> b
applyProxy fn x = fn (Proxy :: Proxy p) b
infixl 2 applyProxy as @$
And then
foo = myFn @$ @"Something" @$ @Int 24