What Purescript needs

I’ll note that I think it is extremely unlikely that PureScript would ever get anything like anonymous union types. I can guarantee you that it would be quite difficult to retrofit onto the existing type system. I think it’s much more likely that it would get something like polymorphic variants, as that works nicely within the type system as is. Rows are a slightly less ergonomic alternative to subtyping (since you must use explicit abstraction in some way to thread around the tail), but it fits very nicely and easily into a unification/equality based type system. I think you’d really want something like bi-unification (https://lptk.github.io/programming/2020/03/26/demystifying-mlsub.html).

In general though, I think you can see variant “unboxing” as a backend specific optimization. If you are dealing with a concrete sum of types, then you can make assumptions and optimizations about representations, but as soon as you move into a polymorphic setting, it would need to be boxed into a uniform representation. Rust will do things like this (for example, to make Option<A> be zero overhead when it’s A is non-null).

3 Likes

I don’t see how that follows. Foo is either A -> String or B -> String. There is no way to pass it an A if it’s B->String and vice versa.

In general I didn’t have any expectation for the compiler to canonicalize types. Without canonicalization there’s little complexity. A | B remains A | B no matter what A and B are. To use a value of type A | B you necessarily have to determine whether it’s A or B after which time the compiler treats the value as of type A (or B).

2 Likes
data A = A
data B = B

type Foo = (A -> String) | (B -> String)

useFoo :: Foo -> String
useFoo k = k A <> k B

I would expect something like that to work.

Edit: Or maybe it’s the other way, maybe that should be intersection? I never remember which is co and contra variant, but that’s kind of my point.

1 Like

What does the type of k A look like? Does it return an empty string when k contains a handler for B? I don’t quite follow this.

I guess in the terms of sum types the conversion would look like -

data Foo = Either (A->String) (B->String)

data Foo = (Either A B) -> String

Which I don’t think is true.

1 Like

What I’m thinking is, if there’s no additional run-time tag for this, the only way to use this type is to distribute the union/intersection in someway, or to just forbid these sorts of types altogether. With a sum type, you have an explicit injection into a branch with a tag, but you don’t have that with anonymous union types. IMO the compromises you have to make fall apart pretty quick.

1 Like

Heh, discourse is giving me warnings about replying too much :slight_smile:. I suppose we should move the untagged union discussion onto a separate thread.

EDIT: Started a new thread here - Untagged union types

I wrote a bit earlier about disambiguating between the various parts of an anonymous union type. The last method (using typewitnesses) is general purpose, and the other two are more specific.

1 Like

I think better documentation and learning resourcrs is key here and better tooling.

Let me start with tooling and IDE support purs ide have some quirks for example you can not get types inside a function, that was a bummer for me

And about documentation we need some tutorial , show some of the good libraries for frontend and backend, some examples of each one in production code. Not just toy examples

3 Likes

OCaml may be? There’s Polymorphic variants and Extensible variants.

The manual is still talking to “subtyping”, but it feels more like constraints. For example a function can specify that it accepts any types as long as their constructor is a subset of what the function support.

1 Like

I’m familiar with OCaml variants (I maintain purescript-variant), but row-based tagged unions are specifically not the kind of subtyping that one generally sees with the anonymous unions being discussed.

1 Like

The Purescript book is great. However, I am finding there isn’t a comprehensive guide to the syntax of the entire language. Something like that in the book would be useful. Even an ABNF description of the syntax would serve as a guide to new users once there appetite was wetted.

Additionally, I recently skipped ahead to, “Monodic Adventures: The State Monad”, which introduced a new state monad, State s, in a way oblivious of the previously introduced state monad, ST. As I write this I am suspecting I may have missed some sentence explaining that, type ST = State s for some constrained s. Actually, this terseness in the book is also an issue…it should be more of a conversation. I’ll ignore the fact that the State should have been named, StateConstructor per its function.

One of the things I like about F# is that the core syntax is modeled as function application. However, F# does not have type classes and higher kinds which perhaps permits such syntactic simplicity. However, I wonder whether much of the syntax for defining structures in Purescript can ultimately be of a more consistent simplified form. For example:

thing IDENTIFIER PARM_1 ... PARM_N of CONSTRAINT_1, ..., CONSTRAINT_M where DEFINITION

Here, thing is one of keywords type, newtype, data, class, instance, let. Of course, this may drift Purescript too substantially away from the older Haskell syntax (or that may already be the case but I don’t yet know it).

Finally, forall seems like a thing that should be implicit. However, there does seem to be a need to distinquish these free type variable names from other identifiers that may become bound within the module. Unfortunately, the only thing I can think of would be to augment such names, such as by suffixing them with $ for instance, but this also seems like an inferior solution to forall. :slight_smile:

My $0.02

2 Likes

Have you looked through my repo’s syntax folder?

There’s also Parser.y if that’s more of what you were thinking.

2 Likes

Thanks for the link. Great work. I was hoping for something more condense… less of an explanation and more of a map. Much of the semantics of the language exist in its syntax and provide intuition for its usage, at least to English speakers and those with some experience elsewhere.

I am enjoying your, “FP Philosophical Foundations: Type Classes”. :slight_smile:

Update:
The domain diagram in, FP Philosophical Foundations: The Big Picture, seems to imply that pure functions are invoked at startup and can themselves directly call impure functions. It is my understanding that is not the case. Thanks for your work and I learned some good stuff. :wink:

1 Like

Sorry to pop up so late. I did functional programming (in Scala) training when a “consultant” in a professional services company. When I left I decided to A) learn about marketing and b) get back to programming preferably functional. PureScript is on my Sometime/Maybe tag. I also started a Haskell MOOC (and got overwhelmed) and use Hakyll.

The killer app.

I and others in my area (digital business transformation) really need a distributed, autonomous content management system for complex/chaotic pipelines/development lifecycles.

I went back in time and looked at Solid, that is based on web technologies. And was horrified to find that most of the code repos for things like Pod browsers and servers are written in JavaScript. There’s a few written in TypeScript.

I thought, why not a PureScript Solid distributed CMS?

I suspect it would knock the socks off other contenders for this enterprise space as we all know that functional solutions will be less accident prone. And contenders are limited to one at the moment; the Open Services for Lifecycle Collaboration.

Very few of the services in this diagram are complete but they are all defined using RDF and connected with the LDP.

OSLC Service Domains

Build one front end and you get the lot. It’s what I want and it will replace so many useless but expensive enterprise toolchains that the legacy enterprise behemoths sell to legacy enterprise dinosaurs.

I looked at the summary repo and there is only one killer app. Rails doesn’t fit my definition of an app. Well not like Lotus123 was.

Is it worth considering?

type Foo = Tuple (A -> C) (B -> C)

would be canonicalized to

type Foo = (A | B) -> C
1 Like

/rant
Well, let’s admit. People and businesses just need to get things done, however haphazard and ugly it would be. Bugs? No, they are features. Just handle problems in ad-hoc way, and build from ground-up when it goes out of hand. In the end, easy-to-learn languages (python) dominate the field because of cheap labor they can accrue - for this reasons, companies are even veering towards no-code solutions. Hence, I do not see much way out for FP to become mainstream.

Said that, we could strive for attracting considerate ppl at least - who would provide value to the community.

2 Likes

I don’t agree. At least, not completely.
You are right, easy-to-learn languages dominate the field where you need to get things done fast. Ad-hoc scripts, models built from ready-to-use blocks (like scientific libraries), quick data analysis - yes, I’d use Python (or R), in less than 50 LOC I would have data analysed and visualised the way I need.

However, I cannot imagine maintaining ~50kLOC backend code in Python. Refactoring, adding new functionalities, etc. In functional language, you can bring a junior dev with no experience to refactor a small bit of your code and he will manage. No hidden state, no mutability, he just rewrites one pure function to another pure function. You are assured by the compiler that the change is local. You can test it easily with things like quickcheck.

Prove me wrong, but rarely have I seen job offers for backend developer with Python. You usually see Java, sometimes C#. And Haskell / PureScript beat the sh*t out of such languages.

We were actually talking about the sum (A->String) | (B->String) rather than the product (Tuple).

I mean, I see the trend. The swarm of easy-to-learn languages, led by devs with shallow experience. Refactor? They do not bother, rewrite the whole if problem occurs. Costs less thanks to the cheap labor! These days, companies could also utilize machine learning to implement functions, they just want it barely working.

It is already happening in fringes, and climbing up to the main usecases.

Tuple (A → C) (B → C) ≅ C^A * C^B = C^(A + B) ≅ (A | B) → C

1 Like