PureScript v0.15.10 Released

PureScript v0.15.10 has been released!

Only two changes in this release:

  • Visible Type Applications are finally here! (Thanks @PureFunctor!)
  • Better support for colocality (e.g. putting your test code files, Storybook story files, and source code files all in the same directory while still compiling them separately). (Thanks @i-am-the-slime!)

Full release notes below:


TryPureScript and Pursuit have been updated to use purs@0.15.10. Happy hacking!

1 Like

What of standard libraries? Are there plans for annotating them with visible type parameters where appropriate? Is any help required with that?

1 Like

So far, nothing has been decided on the core team as far as I’m aware. Adding those would entail updating at least core and contrib libraries, which is a significant effort. So, I’d like to do such changes when we’re doing breaking changes across the ecosystem.

To reduce the amount of effort, we could also update only the identifiers in libraries that most people care about (e.g. record-related things, Proxy-related things, typeclass-related things, etc.). As an example, making things like Record.get @"fieldName" or V.inj @"tag" work would likely account for a lot of the pain points that VTAs addresses while reducing the amount of work needed to update the ecosystem.

However, we have to be careful when updating things as the order of the type parameters in the forall can now be considered a breaking change. For example, let’s assume we released the following:

foo :: forall @a @f. f a -> f Int
foo = ...

bar = foo @Int @Array

Swapping the order of f and a would be a breaking change as bar would no longer typecheck.

foo :: forall @f @a. f a -> f Int

bar = foo @Int @Array -- compiler: cannot match Int with Array

In some cases, it’s likely clear which type parameter should come first. In others, it may be ambiguous, and thus bikeshedding would result.

All that to say, we shouldn’t rush it, but it does need to be done to enable the full use of VTAs. Lastly, updating the ecosystem is unfortunately not a parallel job. See previous Discourse posts describing the ecosystem update for context.


Oh my lord! I have been waiting for Visible Type Applications to drop since over a year.
I haven’t been this excited since Christmas 1998.

I would like to wholeheartedly thank @PureFunctor for making this feature but also @JordanMartinez for powering it through.

As a user, I don’t take this for granted at all.


Thanks for both of these features :heart:

Can someone give a short reason or point me to a discussion as of why there is a difference between visible/invisible for this feature?


I also would like to express my gratitude to @PureFunctor for implementing this. After adding type-level integers this is the second big feature that greatly simplifies my libraries :slight_smile: I’m hoping that polymorphic variants will be the third :wink:
Thanks also very much to @JordanMartinez for all the review work on this and pushing it through!
Third thanks @i-am-the-slime for the colocality support!

For those of you asking yourself what VTAs are and why @i-am-the-slime is so stoked about them:
Previously you needed Proxys to communicate with the type-level, now you can “visibly” apply a type on the value-level.

Two of my libraries, barlow-lens and fast-vect, profit from them very much. So let me give you an example:

barlow-lens let’s you create type-safe lenses for records. E.g. upper-casing the star field in the following record:

sky =
  { zodiac:
      [ { virgo:
              { star: "Spica"
      , { virgo:
              { star: "Serpentis"

upped = over @"zodiac+.virgo?.star" toUpper sky -- With VTAs

Without VTAs, we have to use a Proxy:

upped = over (Proxy :: Proxy "zodiac+.virgo?.star") toUpper sky -- Without VTAs

Much cleaner with VTAs, isn’t it?

fast-vect let’s you create type-safe vectors. E.g. we can create two vectors of different length (encoded at the type-level), concatenate them and then dropping/taking some elements:

as :: Vect 300 String
as = FV.replicate @300 "a" -- With VTAs
--as = FV.replicate (Proxy :: Proxy 300) "a" -- Without VTAs

bs :: Vect 200 String
bs = FV.replicate @200 "b" -- With VTAs
--bs = FV.replicate (Proxy :: Proxy 200) "b" -- Without VTAs

cs :: Vect 500 String
cs = FV.append as bs

ds :: Vect 2 String
ds = cs # FV.drop @299 # FV.take @2 -- With VTAs
--ds = cs # FV.drop (Proxy :: Proxy 299) # FV.take (Proxy :: Proxy 2) -- With VTAs

It appears I still can’t do something like:

class C a where
  x :: String

With the idea of calling it with x @Int. It seems the class parameters have to appear in the signature somewhere.

I can always workaround:

class C a where
  x' :: Proxy a -> String

x :: forall a. C a => String
x = x' (Proxy @a)

But I wonder if there are any specific reasons for this not to work?

1 Like

Another thing I just found: if the applied type has wildcards, they cause the WildcardInferredType warning (same one that wildcards in top-level type signatures do), even when the type is known or can be dropped:

f :: forall @a. a -> a
f = identity

x :: { x :: Int }
x = f @{ x :: _ } { x: 42 }
Wildcard type definition has the inferred type


in value declaration x
1 Like

Can you open an issue for this? That seems like something worth discussing.

1 Like

For both issues? Or did you mean a specific one?

This morning, I was actually thinking of something like this, too. We currently have the Reflectable type class:

class Reflectable t v | t -> v where
  reflectType :: Proxy t -> v

-- which allows
foo = reflectType (Proxy :: Proxy "foo") :: String

Ideally, this would become

class Reflectable t v | t -> v where
  reflectType :: v

foo = reflectType @"foo" :: String

but I’m not sure if that works or not because t isn’t mentioned anywhere in the reflectType member’s body.

1 Like

Theoretically there is nothing here not to work. The method is part of the class dictionary, so as long as the right dictionary is chosen based on the applied type, it doesn’t matter what the method body is.