All you need is data and functions
mckayla.blog
mckayla.blog
To take the example given for gleam, there is nothing enforcing that the conversion to string is called to_string, what are its arguments, how formatting works. So you're left to just-so compatibility for each type. They only happen to be the same by accident, and nothing enforces consistency.
Programming language and code in general is about communication between programmers. When I design a trait, or an interface, I'm telling everyone else what are the requirements.
Note: Saying "similar mechanism" is a tautology for whatever you defined after that.
You don't need a class/trait/etc enforcing a contract on data. Instead you can keep data and functionality separate, and then prove you have an implementation of function X for data Y.
This also allows for easy extension of the data downstream.
If one really wanted to argue, that would be that instead of saying that traits/interfaces are types, rather one should say that types are interfaces over raw data.
An interface/trait would then supersede the concept of type.
So much so it became something of a meme to say "just re-write it in Go" for a while.
But a lot of companies are institutionally committed to the jvm, and/or have deep expertise within it, and a lot of those have smaller teams or individuals with more freedom to experiment with their tools.
And now clojure has settled into a comfortable niche as the language you use when you should be using java but just don't wanna.
This is one path to making it easier for devs to justify using a new language. And I mean, typescript was in this situation more or less at one point. So sure erlang isn't as popular as java or javascript, but it is a runtime that some places are committed to and have expertise in. I could see gleam latching onto that, maybe.
An opposite trend I've been noticing is that big languages steal ideas from some smaller language and absorb its community: for example a lot of R ideas have been pulled into Python libraries, resulting in a migration from R to the more capable Python. R's syntax is nicer for statistical computing, but having access to the larger Python ecosystem with slightly worse syntax turns out to be more valuable in the long run.
> Why did someone create Gleam and how is it being used in production today?
I'm an outsider to this community, so I'd trust someone with inside information over myself, but my outside impression is that Gleam was invented to push Erlang's potential for extremely high reliability even farther. Gleam's strong type system lets you create software that rarely fails, and on top of Erlang/OTP's "let it fail" philosophy you have battle-tested patterns for recovering from failures when they do happen. The use case is for networking software which requires high levels of security, scalability, and fault-tolerance, such as cryptocurrencies and end-to-end encrypted messaging systems (it's used by WhatsApp and I think the NEAR cryptocurrency though I can't find a reference for the latter claim).
> I have a hard time imagining going to a team or customer and saying “
- c#
- go
- java
- python
- javascript
ARE wrong ... for this project.
---
So, what people don't get is that you need somebody crazy enough to make `go, python, java` when C, Cobol and other existed.
You see the major success, but not the attempts in the middle.
For people like us that do languages, not just for fun, we are trying to be the NEXT on that list!. And if somehow one of this languages break it the barrier is because, for certain, they are better than most, at least at certain niche.
---
> But I just wonder about the real world uses and actual stories behind new and lease known languages
A little secret: Creating a new language is the ONLY way to make a paradigm work at scale. You can't do OOP in C, not practically. A new language is the next step after make a framework, and you can see it as the only way to make the idiom of a large scale framework to truly shine without the baggage.
So, in my case (https://tablam.org), I have used +20 programming languages (including SQL dialects that inflate the number!), for real, for work, for pay. None is close to solve the needs of build business apps, and the one that was close (FoxPro) die too long ago. None is relational, none is good for data manipulation, none allow me to encode the business checks, flows or invariants, or make data reports, etc.
"None" here is too strong of a word, right? All that other languages I have used have shipped code! So they "works!", but none allow me to truly embrace the relational paradigm, aka: deal with data.
I can only speak for myself, and I am not involved in the creation of Gleam. But I have to say, an ML style language on top of the Erlang VM (BEAM) is a very enticing combination to me.
I'm also not using it in production at ${dayJob}, so take that for what it's worth. Maybe some day I'll get the opportunity.
There are some exceptions where classes need to be instantiated, and I'm thinking, instead of writing a bunch of ser/de, I can just make them static, Replace-All `this` to `self` and add a `self` param. Boom, suddenly save/load is a simple as JSON.stringify() and JSON.parse()
Then I realized I'd reinvented C (plain old data) and Python (explicit self param). Nothing new under the sun!
Well, yeah, obviously, when you have a single implementation of a trait/interface, then you can do away with this additional indirection, sure. But what if I have vastly different types implementing the same trait and I want to collect them all in a single list?
> If you need some data-type specific logic, then pass around functions as necessary (usually from your conversion function).
That... basically amounts to manually bundling values of the concrete type together with the implementation of trait and then passing them around together (also manually), isn't it?
I mean, on one hand I get the message: you don't really need any fancy type systems, having integers and bytes and arrays of integers and arrays of bytes for types, ability to declare constants, global and local variables, and functions (not closures) is quite enough already to implement anything. Structs then can be emulated either as short integer arrays of fixed lengths, or with SoA approach: all enums are just integers, and pointers are also but an integer index in some globally known array so yeah, there is nothing much else you need to store in a struct's field.
But let me tell you, programming this way is not very pleasant if you've spent majority of your life writing Python or Haskell. Having common patterns of data and code organization baked straight into the language is nice.
That is more or less similar to implementing the Go interface pointer or Rust "dyn" fat pointer manually. And of-course everyone will do this their own way instead of the language providing the standard.
I make a bet that next major version of Gleam will have traits once its designers are enlightened on the need for the same.
I'm not sure why this appeals to so many people, but still: it clearly does. So, best of luck to the Gleam team, I hope it lives up to your needs and desires!
CDisplayInterface* louis = NULL;
HRESULT hr = friend.Friend("Louis")->QueryInterface(CLSID_DISPLAYINTERFACE, &louis)
CheckError(hr)
io.println(louis->format(DisplayOptions(...)))
basically the same thing as Gleam does, but more verbose and without all that safety.(I think anyone advocating "going back to basics" would benefit from actually going back to basics and spending some time on those systems. I hope I will never have to touch COM in my life again, but my experience working with it was very helpful. GLib/GObject and Linux VFS are other class-like technologies which are worth experimenting with)
|> iterator.from_list()
|> iterator.map(Friend)
|> iterator.map(friend.to_display)
|> print_things_from_an_iterator()
This only prints Friend types. How would you loop through a collection of several types and display them? In other languages they could all implement the interface. Perhaps you could have several to_display functions that handle each type but I'm not sure that's valid in Gleam. It would need runtime type checking. In this example to_display is a member function.But type-classes get a dual use also as constraints for parametric polymorphism. This gives you some more immediate feedback from the type checker and also lets the programmer extend type checking with their own rules.
Never the less I sometimes use this pattern of grouping functions together into a record along with pointers and handles they need to shared resources to make running those functions, passing them around, and cleaning them up easy and explicit -- when it is called for.
From experience I find I doubt that we tend towards complexity or enjoy writing complex code. Most people I know find complex code takes quite a lot of effort to write, understand, test, and make useful. The majority of people I've worked with want to write simple, elegant code and find joy in understanding a problem. It happens that some times we either judge the problem to be more simple than it is or we can't formulate a succinct specification of the solution. This leads to code needing some solution to the inherent complexity in the former case. And in the latter it means we don't know what we're doing and are getting lost in the weeds. In both cases a liberal application of good-old-fashioned thinking tends to do the trick.
When thinking is insufficient it always helps to have coding guidelines, mentors, and access to experienced colleagues. The last ditch effort of course is searching the good, old Internet.
I use Rust, C, Python.
For example - functions and methods organise the procedures in smaller, more manageable (from human perspective) chunks, types formalise and limit the data to make them easier to transfer and organise, and classes (are supposed to) do both.
The idea of traits as types is potentially interesting from a pedagogical perspective, since it gives us another way of thinking about things. But whether it is simply analogous or is in fact homologous is the key question: If simply analogous, then we need to understand the limit of the analogy, where it works, where it fails. If homologous, then we have a powerful tool to draw upon, much like how mathematicians can switch analytic techniques equivalent over whatever category they are working with, or how physicists change coordinate systems to make the math and understanding easier for particular types of motion.
I tend to think it is simply an analogy, but I don't yet know where its limits lie.
> Really the only difference is that we manually convert from Friend to Display in Gleam, where Rust already knows how to use Friend as a Display.... Instead of a trait, just make a type that implements the generic behavior you want, and then write a function to convert your data-type into your trait-type.
Aaaand here's where my gut twists. Why? Why would you do that? Why would you ever do "instead of... -> just" where the compiler will do it for you?
Twice in that quote the author espouses writing more code because reasons, and, honestly, I don't think the reason is any better than "Gleam doesn't haven't traits and we're all OK with doing the extra work".
That's fine. If that's how someone wants to work, and if their code works for them and is as maintainable as they need it to be, great! I cannot gainsay that.
And I'll note that for someone who hasn't yet grokked generics and traits, the Gleam code may be more readable, because things are more spelled out.
Sort of reminds me of my early Javascript, before I got comfortable with promises, composition, lambdas, and a few other things: Very readable blocks of code, often many lines long, that I would now replace with a single line.
But once one understands traits and constraints, one can write very powerful, and, thanks to how Rust works, safe code that other (oh, I do have to use that term? oh, ok) Rustaceans will grok quickly, code that will likely also be performant.
If I can save myself time and screen space AND make it clear to others what's happening AND make it safe and performant, I'm in.
(I'll just remember to add comments as to why this is a single line and why that isn't.)
(EDITS: Typos)
One of the biggest headaches in software (historically) is how things have been named. ("Dynamic programming" says hello).
What is a trait?
Traits: Composable Units of Behavior, 2002
https://scg.unibe.ch/archive/papers/Scha02bTraits.pdf
--
"A trait contains a set of methods that implement the behaviour that it provides. In general, a trait may require a set of methods that parameterize the provided behaviour.
"Traits cannot specify any state, and never access state directly. Trait methods can access state indirectly, using required methods that are ultimately satisfied by accessors (getter and setter methods).
"The purpose of traits is to decompose classes into reusable building blocks by providing first-class representations for the different aspects of the behaviour of a class."
That's descriptive of 2002 and is a long time ago, as far as I'm concerned.
Traits can modify state with the syntax to modify memory, so this definition is of limited use, imo.
A trait is, for all, or at least most, intents and purposes, an interface that can have an optional default implementation of some of the functions inside it.
Traits, interfaces and types can also, for all, or at least most, intents and purposes, play the same role. Define a contract of stuff implemented by a code entity.
Of course any added special semantics depend on the language.