Stream: ideas

Topic: platform interfaces / standards


view this post on Zulip Lawrence Job (Nov 21 2021 at 22:15):

Hi all! This is a really broad question that popped into my head last night, and I am guessing that it's already been thought about, but I can't find where: is there a reason why there is no platform interface apart from the declaration inside a platform?

To be able to write Roc code that sits on a generic "web server" platform, and be able to substitute the platform between, say, fcgi, http, and a test harness would be infinitely valuable for practical systems for both portability and testing. I know I could probably swap platforms and just see if it works, thanks to the way the platforms declare their interface, but to have some abstraction would really help, especially for library development

Another example might be, say, an Arduino SDK for education that's general enough to be able to target any avr microcontroller and knows not to implement something hardware specific thanks to some kind of standard

This would have implications for a package manager, because a good package manager would need guidance to know which packages are compatible. Rn my understanding is "library which targets std lib or library which targets specific platform"

Really interested in thoughts, or if anyone can point me in the direction of an existing discussion that I can catch up on

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:32):

we've talked informally about the idea of ways for platforms to share code, e.g. a way to define a common concept of "this platform has the capability to make HTTP requests according to this API" and then you can write a package which can be used by any platform which supports that, not just one specific platform in particular

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:37):

I want to be cautious about this design space though

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:38):

there are a lot of things where I can look at other, more mature languages to see how things have gone, and have a reasonable amount of confidence about certain problems that are likely to come up for Roc in the absence of certain features that could mitigate them

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:38):

but platforms and applications are uncharted territory; I don't know of any other language that has done this

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:39):

so there's a definite risk of adding a feature that ultimately turns out to have been unnecessary complexity in retrospect, but by the time we realize that, it's very painful to remove because a few projects are relying on it

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:42):

one thing I like to think about is trying to find precedence for similar things in other languages - for example:

be able to substitute the platform between, say, fcgi, http, and a test harness would be infinitely valuable for practical systems for both portability and testing

I don't know of any languages where people do this today, but maybe I'm missing some! For example, I'm familiar with web servers in the Java, JavaScript, Perl, Ruby, and Haskell ecosystems, and I don't know of this being a thing people do (for either portability or testing) in any of them, but maybe it happens in other ecosystems I'm not aware of! :big_smile:

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:43):

an Arduino SDK for education that's general enough to be able to target any avr microcontroller and knows not to implement something hardware specific thanks to some kind of standard

I have basically zero Arduino experience, but I like to think this would be a thing a single platform could do, in a similar way to how someone might make a single platform for building desktop UIs on macOS, Windows, and Linux even though all of those have different APIs etc.

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:44):

so I guess overall my feeling with this design space is that it's worth having some general thoughts on how to do it, but the risk of implementing a solution to perceived potential problems is unusually high in this circumstance because there's no real direct precedent to draw on from other languages

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:46):

if I'm stepping back and being honest, I think probably the right time to implement a feature like that is after people have been saying "this feature should have been in the language long ago; what's taking so long?"

if people are saying that and meaning it, it's probably a good sign that we were sufficiently patient given how little precedent we have to go on :big_smile:

view this post on Zulip Richard Feldman (Nov 22 2021 at 00:46):

and if not, we probably jumped the gun

view this post on Zulip Lawrence Job (Nov 22 2021 at 01:26):

Thanks for taking the time to reply - that was super helpful. It definitely seems like something that can wait for v1.1/v2.0 if a need is demonstrated (although maybe considering it as a possibility today might save headaches - and maybe save some effort with code-gen for bindings).

view this post on Zulip Lawrence Job (Nov 22 2021 at 01:27):

I notice myself, from an architectural perspective, constantly losing track of how much encapsulation the app v platform divide should give. In the case of 'console,' 'tutorial,' 'edge processing,' 'domain-specific plugin' it's obvious that the API is very specific to the domain. Meanwhile, in the case of more generic platforms like 'windows-service-x86' or 'wasm' it would probably be a much wider scope.

view this post on Zulip Lawrence Job (Nov 22 2021 at 01:28):

In the case of arduino, they're actually built on wildly different CPUs with totally different instruction sets and special memory addresses. It's possible that there could be a really wide API surface area that just offered low level read/writes and then have the HAL sit in Roc, or it's possible to built the HAL in Rust/whatever is used lower level and expose a really narrow API surface to the Roc app. I'm not sure which is best, if either

view this post on Zulip Lawrence Job (Nov 22 2021 at 01:30):

Fwiw, I am not sure if Arduino is even supported by the toolchain because LLVM doesn't officially* support avr, so it might be hypothetical - maybe Roc isn't interested in dealing with the complexity of targetting embedded systems at this point?

view this post on Zulip Richard Feldman (Nov 22 2021 at 02:05):

Meanwhile, in the case of more generic platforms like 'windows-service-x86' or 'wasm' it would probably be a much wider scope.

to be honest, I suspect that making a platform that broad would be a missed opportunity

view this post on Zulip Richard Feldman (Nov 22 2021 at 02:05):

like why would I use "The Windows Platform" to make my desktop UI if there's a Windows Desktop UI platform which is more tailored to my use case?

view this post on Zulip Richard Feldman (Nov 22 2021 at 02:06):

it would be like if someone made a framework for node.js called "Windows"

view this post on Zulip Richard Feldman (Nov 22 2021 at 02:06):

I'd think that would be so broad as to be less useful than a more focused one! :big_smile:

view this post on Zulip Richard Feldman (Nov 22 2021 at 02:10):

I notice myself, from an architectural perspective, constantly losing track of how much encapsulation the app v platform divide should give.

I think there are likely to be two common ways to use a platform:

  1. At the same level of abstraction as a "framework" in most languages - so, for example, someone writes a Roc web server platform that has an API that's at a similar level of abstraction to something like Express in Node.js, or Sinatra in Ruby, except that it also ships with the API for making effects like HTTP requests as well as handling them.
  2. "embedding Roc instead of Lua" - where you're writing a big application in a systems language (e.g. a high-performance game written in C++ or Zig or Rust) but you want a higher-level language for some parts of it that aren't so performance-intensive (e.g. a menu system in a game), so instead of embedding Lua, you make your entire game the platform and write an "embedded" Roc application for the less performance-intensive parts

view this post on Zulip Richard Feldman (Nov 22 2021 at 02:12):

I don't predict there will be significant demand for platforms that are broader in scope than a framework would be in most languages, because anyone building an application would get a better experience (because of domain-specific memory management, if nothing else!) by choosing a more focused platform

view this post on Zulip Richard Feldman (Nov 22 2021 at 02:17):

Fwiw, I am not sure if Arduino is even supported by the toolchain because LLVM doesn't officially* support avr, so it might be hypothetical - maybe Roc isn't interested in dealing with the complexity of targetting embedded systems at this point?

yeah I definitely want to keep the use case in mind, but I've heard mixed reviews of the viability of languages that want to do arbitrary heap allocations on embedded systems...really I just don't personally know enough about the space!

view this post on Zulip Sebastian Fischer (Nov 22 2021 at 06:20):

My impression is that Abilities (proposed by Richard elsewhere) would be able to describe APIs also for platforms, so Roc may not need a separate language feature for that. Whether it would be worthwhile to specify the API of a platform using Abilities is a different question. As Richard points out, in languages with similar features (interfaces in Java, type classes in Haskell, traits in Rust) they are rarely used on a framework level.

view this post on Zulip Lawrence Job (Nov 22 2021 at 16:23):

Richard Feldman said:

like why would I use "The Windows Platform" to make my desktop UI if there's a Windows Desktop UI platform which is more tailored to my use case?

Ah yeah agreed - I messed up my words there. I guess I'm trying to compare whether Roc is designed around a "UI platform" vs, say, "windows-specific UI platform" or "webserver" vs "nginx module" (if that's a thing?) - and speculate about whether there's even merit in me thinking about it that way

view this post on Zulip Lawrence Job (Nov 22 2021 at 16:24):

Once again thanks so much for making time for my philosophical questions

view this post on Zulip Richard Feldman (Nov 22 2021 at 16:46):

absolutely! :smiley:

view this post on Zulip Joseph Anthony Zullo (Nov 22 2021 at 21:28):

Some things I've been wondering about is the interaction of libraries/packages and platforms. I know that it's been said previously that packages will have to be by platform, since they may use that platform's IO, but clearly this depends, a containers library should be accessible from any platform, whereas a GUI library should be more specific than that.

Also, when we are very specific like some kind of Windows platform, we lose being cross-platform in a use-case where we probably expect to be. In cases where we actually can't unify the interface between platforms, I think there will have to be some streamlined way to port things.

What I'm getting to basically is it looks like we need some platform hierarchy. If we have something like global roc -> windows -> windows GUI then we ideally want windows and windows GUI to not be separate forks but for the second to properly build off the first. Then we want packages to belong to the correct place in the hierarchy, containers to global roc and OS specific things to the windows platform. There's also the case of a branching hierarchy for multiplatform support.

I think that platforms in general is a cool idea but we should be careful to make sure there is coherency between their offerings. If we have platforms for varying levels of file safety, windows GUI, and audio, we wouldn't want 8 different platforms to cover all of these cases. They either have to be highly modular or stay broad enough to not run into these issues. I apologize if this stuff has already been more or less thought through but it's something to consider if it isn't.

view this post on Zulip Brendan Hansknecht (Nov 22 2021 at 21:34):

I think that most likely platforms will not build off of or depend on each other. So windows GUI would not depend on windows generic. I think more likely, we want to find a good way to make sure that they can share interfaces. I think we would more likely want windows GUI and linux GUI to share an interface.

This does not actually mean the platform itself has to expose the same interface to Roc. It just means that they at least need a wrapper library in roc that is part of the platform. This means they could have different primitives exposed to Roc but still be swappable. Of course, the platforms could have the exact same interface as well.

view this post on Zulip Brendan Hansknecht (Nov 22 2021 at 21:35):

I think the hardest part of this will be finding the correct boundary. Most likely some gui platforms will want to be higher or lower level. Thinking about code sharing when you are first defining a platform is quite hard. That being said sharing interfaces is really nice for the community.

view this post on Zulip Brendan Hansknecht (Nov 22 2021 at 21:40):

Also, I think that there was a comment around essentially 2 kinds of packages:

view this post on Zulip Brendan Hansknecht (Nov 22 2021 at 21:40):

I guess roc code built for a specific platform, but not part of the platform could be a 3rd type.

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:50):

from an ecosystem perspective one way to think about this question it is to ask:

I think the answer to the first question is "very little." Frameworks typically share almost no code, unless it's that multiple frameworks happen to be using the same framework-agnostic library.

so for example, there isn't like "Electron for Windows" and "Electron for Linux," there's just "Electron." Similarly, I'd expect someone will make a Roc platform for desktop UIs that works across operating systems. (Maybe separately someone will want to make an OS-specific one, like Cocoa for macOS, but we can tell from framework precedent that it's not a requirement!)

as for the relevant differences between Roc platforms and frameworks in other languages, the big one is effects. You might have two Ruby frameworks which use the same framework-agnostic HTTP request package, for example.

so I think this idea boils down to "is there a way to write a package that uses effects and yet is not coupled to a specific platform?"

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:50):

importantly, it may turn out to be the case that this is possible without adding any language features on top of the ones that already exist

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:51):

to take the HTTP example, in the original elm/http the API was that you would build up a Request, which was a description of the HTTP request you wanted to make, and once you were done, call Request.toTask on it

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:52):

to convert it to a Task

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:52):

it's already possible today to write a platform-agnostic HTTP package that has this design - building up a description of a HTTP request - except the toTask part wouldn't live in the package itself, but rather in the platform

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:54):

so anyone in the ecosystem can build a reusable library on top of this "build up a description of a request" package, and then each platform exposes their own "convert a Request into a Task" function (which may also be sharing host code under the hood written in a low-level language), at which point there's tons of code sharing happening and there weren't any new language features required to achieve it

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:54):

it may also turn out that this pattern generalizes, and is a nice way in general to specify effects in a platform-agnostic way - I don't know, to be honest!

view this post on Zulip Richard Feldman (Nov 22 2021 at 21:55):

but it seems worth trying to do things that way first, and seeing how it feels, before adding features to the language that may in retrospect have been unnecessary complexity

view this post on Zulip Brian Carroll (Nov 23 2021 at 08:33):

That's a neat application of "effects as data"! You could imagine pure Roc packages with types describing things like a POSIX file, socket, etc. Then many different platforms operating on that type. Especially where there's already some industry standard format.

view this post on Zulip Lawrence Job (Nov 26 2021 at 18:49):

Really interesting discussion - how would this work in practice? Would platforms ship with many binaries inside it - one for each architecture? I can't find it, but I remember being told that packages are intended to be distributed in compiled form - is this at IR level or at machine code level? It's much easier to abstract over OS differences than it is to abstract over instruction set differences that come from different hardware.. (if even possible?)

view this post on Zulip Lawrence Job (Nov 26 2021 at 18:53):

I think it's likely that there will be an application hierarchy as @Joseph Anthony Zullo said, but it will manifest inside the platform code - there will be a hierarchy of frameworks inside each platform, building up to one opaque platform -- the roc developer won't have to worry about what stack of complexity is running behind the scenes

view this post on Zulip Lawrence Job (Nov 26 2021 at 18:56):

My first instinct when I saw Roc was "wouldn't it be better if I could add infinite low-level platforms so I can compose an httpclient platform with a serialisation platform, with a console platform or something, etc" but I've managed to persuade my brain to think of it as a clean split and understand the principle of the project - let frameworks in Roc fill the void between application code and infrastructure code

there still has to be a multi-tier architecture in Roc projects, and there's still need for libraries/frameworks to provide that separation

view this post on Zulip Lawrence Job (Nov 26 2021 at 18:58):

I still do think it would be really valuable if platforms could identify as having a composition of 'traits,' letting each platform opt in to some interfaces like 'httpclient' or 'console' etc, but I can hear that it doesn't seem to fit in the spirit of the project

view this post on Zulip Brendan Hansknecht (Nov 26 2021 at 19:01):

Would platforms ship with many binaries inside it - one for each architecture?

One for each os and architecture combination.

view this post on Zulip Brendan Hansknecht (Nov 26 2021 at 19:03):

"wouldn't it be better if I could add infinite low-level platforms so I can compose an httpclient platform with a serialisation platform, with a console platform or something, etc"

This is a cool idea but very hard to do in practice. Each platform is a standalone executable that can be written in any language that can speak cffi. This means that it is very non-trivial to merge or depend on more than one platform.

view this post on Zulip Brendan Hansknecht (Nov 26 2021 at 19:08):

I still do think it would be really valuable if platforms could identify as having a composition of 'traits,' letting each platform opt in to some interfaces like 'httpclient' or 'console' etc, but I can hear that it doesn't seem to fit in the spirit of the project

I think that most people agree this could be useful. I think the big hesitation is on whether or not this should be a language feature. Would be great if many platforms shared similar interfaces and you could switch between them freely. That being said, constraining too much just leads to people making a different interface and these interfaces can be very subjective. Also, a lot of the time, opaque wrapper types are used to define interfaces. So 2 different platforms just need to share the same opaque wrapper types to share the same interface.

view this post on Zulip Brendan Hansknecht (Nov 26 2021 at 19:10):

Like both of these implement the same opaque wrapper around httpclient or console. This means I can use it in the exact same way in multiple applications, or target both with the same application.

view this post on Zulip Brendan Hansknecht (Nov 26 2021 at 19:11):

I think it's likely that there will be an application hierarchy as @Joseph Anthony Zullo said, but it will manifest inside the platform code - there will be a hierarchy of frameworks inside each platform, building up to one opaque platform

Can you expand on what specifically you mean by this? Are you talking about, for example, a rust web platform depending on a hierarchy of frameworks via crates?

view this post on Zulip Richard Feldman (Nov 26 2021 at 19:45):

there still has to be a multi-tier architecture in Roc projects, and there's still need for libraries/frameworks to provide that separation

for what it's worth, my experience in software has generally been that the more "layers" there are to it (for multiple definitions of "layer" as it turns out!) the harder it is to understand what's going on, and also the slower it runs - so I like to encourage removing layers of hierarchies rather than adding them!

one of my hopes with Roc is actually that one platform can take the place of things that are often implemented today using multiple layers.

for example, in an Express.js application you have the JS Express API running on Node which is a layer on top of V8 which is a layer on top of the OS. I've ended up needing to know details about all of these layers to write Express applications in production, and each layer adds runtime overhead.

In Roc, we can collapse all those layers into one platform (with a Roc API), including the OS - like we've been talking about in an #ideas thread

I think that sort of direction is a really promising way to make simpler software that's easier to understand and runs faster!

view this post on Zulip Tim Whiting (Dec 08 2021 at 17:28):

Are platforms supposed to completely replace cffi? I think I heard that somewhere. Personally I feel like we need platform libraries & a platform core.

Real world example: In my research I have a game UI and some agents that use linear programming to solve optimal solutions to the game. I use the UI to evaluate and iterate on my bots. If all cffi has to be through the platform, then I cannot use both the UI platform and a platform that exposes the linear programming c library in my code. I would have to copy one or the other platform and merge them together, rewriting all of the Package-Config and platform files to add in the additional functionality.

My idea:
Allow specifying multiple platforms, but select the ROC memory allocation and main function from one of the platforms. Or have platform libraries which are similar to platforms (can expose Effects, call into C code, etc), but do not have a memory allocation functions or main function. Additionally be able to specify or pin a platform library to a specific thread.

In this way I could choose:

main roc code goes on the main thread by default, but can be set to for example the uiThread.

Not sure what your thoughts are on this, or how you would approach this problem. Obviously I could run agents as separate processes, but currently I use dart which has hot-reload of code while the UI is running, so I can tweak the algorithm and then restart the game and I'm guaranteed that all agents are running the same code & don't have to wait for a full-recompile of everything.

Instead of being a mult-tier or layered architecture, it would essentially be a flat layer of everything that involves cffi. Reminds me a bit of platform-channels and flutter's approach to a similar problem, but done a lot cleaner.

A platform could still be more involved like a game framework or ui framework, but it could be composed of several different platform libraries for handling different aspects of the game runtime (audio / visual / physics, etc) and those aspects could be assigned threads.

view this post on Zulip Brendan Hansknecht (Dec 08 2021 at 17:45):

This definitely needs to be considered more and dug into. The biggest problem is that Roc doesn't really control compilation and the executable. This means that platforms would need to support linking to an arbitrary other platform while building, hoping not to have any conflicting symbols.

The current expected solution would be multiple libraries would be made in the host language. Then the platform would just be a wrapper around those libraries. It could more or less directly expose the ui functions.

view this post on Zulip Brendan Hansknecht (Dec 08 2021 at 17:46):

So for examples. In rust, we don't need every platform to repeat the standard allocators. There could just be a crate called roc-default-allocators that generates them for you. Probably would still want them to specify their own panic, but maybe their would be an optional default for that as well.

view this post on Zulip Brendan Hansknecht (Dec 08 2021 at 17:48):

The other potential problem I think would be common is wrapper/type compatibility. If both platforms want to expose a Task type, but they are different, that has to be resolved somehow. In the simplest case, it could just be name-spaced, but that is not the cleanest solution.

view this post on Zulip Brendan Hansknecht (Dec 08 2021 at 17:53):

So I 100% agree that this would be great, but I think it will be a very hard problem to solve in a fluid manor. I think much more likely it will have sharp edge cases that are way to easy to hit. I also think that host libraries already mostly solve this probably, though definitely not fully. The main issues with host libraries are:

view this post on Zulip Richard Feldman (Dec 08 2021 at 20:00):

so any platform can offer a Task based "FFI" if they want to, by exposing a function like this:

callDylib : Str, List U8 -> Task (List U8) [ DylibLoadErr ]*`

view this post on Zulip Richard Feldman (Dec 08 2021 at 20:02):

so pass it the name of the C dynamic library to load, then some arbitrary bytes (the arguments; C can cast them as needed into the appropriate types) and then C returns a new list of bytes for the return value (which Roc can then parse back into whatever types are appropriate)

view this post on Zulip Richard Feldman (Dec 08 2021 at 20:02):

and of course C can do any necessary side effects along the way, since it's a Task

view this post on Zulip Richard Feldman (Dec 08 2021 at 20:03):

so this is the general idea for how a platform can opt into allowing an application author to invoke arbitrary C code (or Zig or Rust or whatever) that the platform author doesn't know or care about

view this post on Zulip Richard Feldman (Dec 08 2021 at 20:04):

importantly, platform authors don't have to expose a function like this, which is important for platforms to be able to make security guarantees

view this post on Zulip Brendan Hansknecht (Dec 08 2021 at 20:38):

That sounds like a painful user story:

If this is our recommended solution, I think it will be a big loss for the community. I think the recommendation of forking hosts and building a merged host is likely a better solution in most cases

view this post on Zulip Richard Feldman (Dec 09 2021 at 01:44):

I think the recommendation of forking hosts and building a merged host is likely a better solution in most cases

yeah I think that's a totally reasonable position.

Mainly with that idea I was just thinking of "how could it be done" rather than should it be done :big_smile:

view this post on Zulip jan kili (Dec 24 2021 at 03:26):

Can a pure-Roc "library"-style package import types from multiple platforms? I imagine this would be useful for providing multiple similar platform-specific helper functions.

view this post on Zulip Brendan Hansknecht (Dec 24 2021 at 04:09):

I don't think a pure roc library can depend on a platform at all

view this post on Zulip Brendan Hansknecht (Dec 24 2021 at 04:13):

I assume most likely it would be the reverse. A platform would depend on a pure roc library to expose the libraries type. Or an app will depend on a pure roc library and convert the platform type to the roc type the library uses.

view this post on Zulip jan kili (Dec 24 2021 at 04:53):

Hmm, your latter example may answer the question behind my question

view this post on Zulip jan kili (Dec 24 2021 at 04:56):

Regarding a "framework" library providing cross-platform compatibility, I was imagining a library exposing multiple ways of doing something (doActionAForPlatformX doActionAForPlatformY, etc.)

view this post on Zulip jan kili (Dec 24 2021 at 04:58):

That way, in order to migrate your app from platform X to platform Y, you'd just need to change which framework function you import+use (Find+Replace style), without needing to seriously rewrite any code

view this post on Zulip jan kili (Dec 24 2021 at 05:01):

However, if the platform provides low-level effects and a framework-ish library builds abstractions on top of those effects, it's probably best to pass the effects into the library functions as inputs (doThingA : Num, Str, (Num -> Task Str *) -> Task Str *)

view this post on Zulip jan kili (Dec 24 2021 at 05:03):

Then, in order to migrate platforms, you'd just adjust the functions that you pass to the library, so that your app absorbs the complexity of the platform migration in how it composes helpful library abstractions with primitive platform effects - seems very clean

view this post on Zulip jan kili (Dec 24 2021 at 05:30):

(deleted)

view this post on Zulip jan kili (Dec 24 2021 at 05:31):

I'm not sure what the FP term for this is, but I'm sure it's a common pattern for helper function wrappers

view this post on Zulip jan kili (Dec 24 2021 at 05:31):

Idk how error propagation world work... Maybe the input function absorbs that napping, too

view this post on Zulip jan kili (Dec 24 2021 at 05:44):

Library examples:

HttpHelpers.exponentialBackoff : Num, Num, (Request -> Task Response *) -> (Request -> Task Response *)
LoggingHelpers.logTabularView : Num, Str, (Str -> Task {} *) -> (List (List Str) -> Task {} *)
CliHelpers.showHelpPage : (Str -> Task {} *) -> (Command -> Task {} *)

App examples:

response = await ((exponentialBackoff maxRetryCount firstRetryDelayOrSomethingLikeThat someNetworkEffectFromAPlatform) request)
_ = await ((logTabularView maxWidth caption Stdout.line) csvData)
_ = await ((showHelpPage Stdout.line)  listUsersCommand)

view this post on Zulip Brian Carroll (Dec 24 2021 at 11:06):

Yes, a record of functions is a pattern that's used in functional programming. Elm uses it, sort of in the reverse direction to this. You create a Program by passing a record of functions to a the platform.

But I'm just thinking about the right kind of things to use this for. Some of the examples seem like they're mixing together effects and pure functions. For example in functional programming you'd normally break up logTabularView into a pure transformation to get a plain string, and then a log that works on plain strings. And for showHelpPage you'd separate creating the help page from showing it. So it's "functional core, imperative shell". Helper libraries mostly work on plain data, rather than actually calling any effects.

Another approach would be for a library function to produce a Tag saying "what is the next action I need to do". And if it says Log "hello" then your app actually passes the payload "hello" to a platform function that logs strings. That's how the Elm architecture does it.

The exponentialBackoff is a good example though, since it has an asynchronous aspect to it, so it actually needs to work on Tasks. The other two have Task {} * in the signature, which is a hint that they don't really need to be effectful.

view this post on Zulip Brendan Hansknecht (Dec 24 2021 at 17:03):

Regarding a "framework" library providing cross-platform compatibility, I was imagining a library exposing multiple ways of doing something (doActionAForPlatformX doActionAForPlatformY, etc.)

I fell like this wouldn't be the way to go about it. I think that most likely you would want mutiple platforms to expose the same api (thus changing between them is a no op). So my-windows-gui-platform and my-linux-gui-platform would both expose the exact same functions to the user. They would be completely interchangeable. Then in the actual platform, the conversion from the generic form to the os specific commands would be done. Of course, this only works if you control/can modify both platforms, but I think that is the nicest way to expose something like this.

view this post on Zulip Brendan Hansknecht (Dec 24 2021 at 17:06):

In the case you can't control the platform, I think the simplest solution is some sort of initialization function that return a record of functions. That way you only have to change the one init method and all of the calls in your app will change to the other version of the library. This will sadly most likely have some runtime overhead, but we may be able to compile it away. Otherwise, mass changing functions like you mentioned above would also be functional.

view this post on Zulip jan kili (Dec 24 2021 at 22:25):

All elegant solutions!

view this post on Zulip jan kili (Dec 24 2021 at 22:27):

How many of these "library"-ish Roc packages exist? I know that package publishing is a WIP, but is there a list of existing repos?

view this post on Zulip Brendan Hansknecht (Dec 24 2021 at 22:31):

I think the answer is essentially 0. Most people are only working on apps and platforms. The focus is more on your platform for what you want to make then on trying to share with others. just so early with a lot of testing still going on

view this post on Zulip jan kili (Dec 24 2021 at 22:44):

That makes sense, and I imagine that most broadly-useful highly-reusable code would make more sense to be written in Rust than in Roc.

view this post on Zulip jan kili (Dec 24 2021 at 22:46):

For example, if someone wrote a highly-optimized Roc library for common machine learning algorithms+patterns, I would expect that most ML-focused apps would decline to use it because they'd want those algorithms to live in their platform layer

view this post on Zulip Joseph Anthony Zullo (Dec 25 2021 at 15:13):

Well, that's an interesting question. If someone wants to call BLAS for example, I guess that would only be possible if the platform provides a wrapper for it?

view this post on Zulip Brendan Hansknecht (Dec 25 2021 at 16:32):

That is the case

view this post on Zulip Brendan Hansknecht (Dec 25 2021 at 16:34):

If you could call it otherwise(at least with the current model), it would have a few main complications.
1) side effects isolation
2) interoperability with the platform
3) compilation model

view this post on Zulip jan kili (Dec 25 2021 at 23:00):

Brian Carroll In response to your helpful thoughts on a purely-functional interface, I started redesigning my Rocli experiment as a library of pure functions and ran into a particularly nasty panic for multi-file projects. If anyone has any advice for that, it would be much appreciated!

view this post on Zulip jan kili (Dec 25 2021 at 23:03):

Semi-related, are type aliases intended to be importable? The above panic is preventing me from confirming that experimentally.

view this post on Zulip Brendan Hansknecht (Dec 25 2021 at 23:04):

Yes, or almost certainly yes

view this post on Zulip Brian Carroll (Dec 26 2021 at 13:16):

Oh I'm glad that was helpful @JanCVanB ! The arg parsing library approach sounds like a great way to go! :+1:

view this post on Zulip Artem Shamsutdinov (Feb 04 2022 at 07:18):

https://github.com/Kindelia/HVM

Really interesting stuff. Maybe there are ideas worth stealing from them

view this post on Zulip Brian Carroll (Feb 04 2022 at 10:24):

Just reading about HVM, it's extremely cool! I love the HOW.md document!

view this post on Zulip Zeljko Nesic (Feb 04 2022 at 10:48):

https://camo.githubusercontent.com/fb8b79fb65f6b0431ec5f211a1128ec556bd24054342581f4812889037771d8c/68747470733a2f2f632e74656e6f722e636f6d2f6d6433666f4f554c4b474941414141432f6d616769632e676966

view this post on Zulip Sean Hagstrom (Feb 18 2022 at 12:01):

Hello :wave:, I hope this is the right place to ask this question, but has anyone looked into Haxe: https://haxe.org?

It’s another programming language that compiles to many targets, and while it’s likely not the same architecture as Roc, it could be a useful reference for integrating a Host language to Target environments.

We may also be able to learn from their audience of programmers, like why they need multiple targets and what kinds of applications are most popular.

From what I’ve seen so far, game development is a large interest, which could be for several reasons but these stood out to me:

  1. The language author is a game dev
  2. Games target a variety of graphics libraries supported by different platforms
  3. Devs like tools that abstract over these platforms and need portability for as many devices as possible

Overall, I’m interested in these kind of cross-platform frameworks, and would like to do graphics work with Roc. Please let me know what you think, and look forward to tinkering on this more.

view this post on Zulip Anton (Feb 18 2022 at 12:34):

Interesting side note: me and Richard are working on a gui-example that connects Roc with wgpu. The finished setup can likely be adapted for more advanced graphics stuff.

view this post on Zulip Brendan Hansknecht (Feb 18 2022 at 15:05):

"targeting JavaScript, C++, C#, Java, JVM, Python, Lua, PHP, Flash, and allows access to each platform's native capabilities. Haxe has its own VMs (HashLink and NekoVM) but can also run in interpreted mode."

That is an intense amount of support. Does that mean that library authors might have to write backend code for all of those platforms?

view this post on Zulip Brendan Hansknecht (Feb 18 2022 at 15:07):

Like if you someone trying to write a generic haxe UI library, and you didn't want to force everyone who uses your library to have to use the same backend.

view this post on Zulip Brendan Hansknecht (Feb 18 2022 at 15:23):

Also, from first glance, the biggest difference is that haxe compiles (probably transpires in most cases?) to many targets (generally other languages) while Roc always compiles to assembly.
Considering a Roc platform a target would kinda be like considering a c++ static library as a target. Roc's more has a weird FFI standard through platforms and just depends on them for all effects.

view this post on Zulip Brendan Hansknecht (Feb 18 2022 at 15:25):

Though analyzing how their "more native" libraries support so many targets might be really helpful in designing better apis for certain things.

view this post on Zulip Sean Hagstrom (Feb 18 2022 at 23:16):

I think a simple Haxe lib of utility functions wouldn’t need deep knowledge of the target platform. But something that binds to a certain external library may only be usable on a specific platform.

For a Ui library, I suppose you could define the high level logic to be shared, and the low level logic to coordinate the different implementations for each target. There are a couple game engines for Haxe that might take this approach: https://haxeflixel.com/ and https://heaps.io

In general, I was hoping that the similarities between Haxe and Roc could be found in the way the Haxe compiler transpiles the semantics for each target. I imagined that maybe there were some useful insights for how to model a complete Haxe target / Roc platform.

Atm I’m not sure what the needs of a Roc platform are. Interested to hear some thoughts on this since I noticed some Roc examples involved defined operations for copying memory and other glue code.


Last updated: Jun 16 2026 at 16:19 UTC