imagine if "process state" was a separate type that you'd have to explicitly pass as an argument to a function if that function wanted to do things like setuid, fork, change signal mask, etc.

@wolf480pl I was thinking of doing a "hyper-pure functional language" experiment that would work something like that. Having strict pass-by-value semantics (no pointers, at all), and a main function that returns it's arguments for processing in the language runtime, which is the only place that can interface with the world would naturally lead to libraries explicitly requiring objects representing external state to them.

@wolf480pl Not quite, I don't think. Tho I'm not well versed in traditional functional languages so might be wrong. this is what I'm thinking off.

@ignaloidas oh, so a MonadState RealWorld, where RealWorld is not opaque?


hmm ok, what is the most functional programming language you know?

@wolf480pl none. Python is the one I know best, but that's not functional, C is far from functional either, others I don't know well enough to know how they work.

@ignaloidas wait... so you know no language with real types?
oh dear...

@wolf480pl Python has some very real types, just that the static kind deny their existance :P

Hmm ok I guess I'll have to explain it from scratch. Would you prefer if I borrowed C++/Java's (List<T>) or Python's (List[T]) notation for generics, or just go with Haskell's (List t) ?

@wolf480pl Any but haskell is fine. Do note that I do have a fair bit of knowledge of types, but not the languages that use them, so no need to ELI5.

@ignaloidas ok, so let's go with List<T>.

I'll be using arrow for functions though, so instead of:

first : Function<List<T>, T>

it'll be

first : List<T> -> T


consider the following type:

type State<S><R> = S -> (R, S)

a function that takes state or type S, and returns a return value of type R, as well as a new state of type S

you can define some nice operators on it that'll make it possible to chain it nicely (then we'll call it a monad), but I'll elaborate on that later.

So what you're saying is, main is a State<RealWorld><int>,

and RealWorld is some non-opaque thing that consists of stdin, stdout, and other things?

@wolf480pl Yeah, somewhat, just with RealWorld being defined by main itself (no predefined structure).

@wolf480pl no. It's run again and again until a sentinel value (not pictured in the diagram) is set to some exit code.

@ignaloidas even more interesting...

and because main is pure, it will never block...

I think you'd need some syntactic sugar for it to be able to not have a huge switch(state) at the start, or sth like Python's generators...

how do you read from regular files though?

Do you somehow return a request to read?

@wolf480pl it's quite naturally building a state machine, so having a good switch syntax is a requirement either way. Of course having a large switch is still annoying, but I'd imagine this approach would be more fitting for server applications anyways, where besides startup you don't have many different states.

as for files, you'd just have file objects returned to runtime with appropriate fields written, and the runtime returns them back with what's needed.

@ignaloidas sometimes it's natural to keep state in the (metaphorical) "instruction pointer", especially when you have some form of async io.

Eg. it'd be more convenient to write an entire handshake with a new client as a single linear piece of code than breaking it up into steps.

Anyway, so your idea boils down to main returning "syscalls" it wants to do, and then being called back with the results?

It's pretty cool IMO, and would lend itself well to all kinds of things:

the runtime could be:
- a minimal wrapper that sends everything over a ring buffer to a real runtime on a different CPU (spectre mitigation, heterogenous virtualization, etc)
- a minimal wrapper that sends everything over a socket to a real runtime on a different machine
- a different program whose main() is the real main, and it's only pretending to be the runtime to you
- an async io implementation that allows you to wait on multiple syscalls in parallel w/o changing code

@ignaloidas it seems so good I can't believe nobody tried it before, but I'm afraid it could've been nodejs and nodejs is shit so...

@wolf480pl oh, and for why nobody tried this before, I believe a reason for that could be the fact that this only really makes sense for functional languages, and from my cursory look, most functional language people seem to be allergic to the idea of a runtime.

@wolf480pl GC isn't the same as a runtime. Golang has a GC and I don't think it has anything more that could be called a proper runtime.

@wolf480pl code in charge of executing your code and providing it with some set of utilities.

Consider It:
- provides some utilities
- calls your code from __libc_start_main
- lazily resolves calls to dynamically linked functions

Is this a runtime?

@wolf480pl Yes, I do believe that's a runtime. From a cursory look I also think that most functional languages try to avoid having something like that.

@ignaloidas if you remove dynamic linking, is it still a runtime?

@ignaloidas ^ they call it a runtime themselves, and it does *a lot* of stuff

@wolf480pl oh, huh, now that's a runtime, not sure how I missed it.

I guess a better distinction could be that most functional languages aren't designed with a runtime in mind.

@wolf480pl somewhat. It's important that it's not a library, and depends on the initial setup.

@ignaloidas @wolf480pl Golang has a pretty big runtime to schedule the thousands of goroutine into the real threads. It's also called runtime by them and is added to all generated binaries (that's why small go programs are quite big in binary size).

Sign in to participate in the conversation
Gamedev Mastodon

The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!