Skip to content

Instantly share code, notes, and snippets.

@i-am-the-slime
Created April 23, 2023 16:12
Show Gist options
  • Save i-am-the-slime/754fe5acbc6a3468cf38f29420dd2108 to your computer and use it in GitHub Desktop.
Save i-am-the-slime/754fe5acbc6a3468cf38f29420dd2108 to your computer and use it in GitHub Desktop.

Motivation

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"

How to define functions that accept VTAs

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.

Multiple VTAs

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))

Limitations of replacing Proxy with VTAs

In some cases you will still need to require Proxys 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment