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:
19 Likes
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.
4 Likes
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.
4 Likes
Thanks for both of these features
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?
2 Likes
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 I’m hoping that polymorphic variants will be the third
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 Proxy
s 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:
Just
{ star: "Spica"
}
}
, { virgo:
Just
{ 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
5 Likes
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
Int
in value declaration x
PureScript(WildcardInferredType)
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.
2 Likes