10 ms·
Hi, Developer here. I was going to post this here in a couple of weeks after launching the product and creating a separate site for the language with much bett
by amedvednikov 8y ago
Hi,
Developer here. I was going to post this here in a couple of weeks after launching the product and creating a separate site for the language with much better information about it.
I'd also like to hear your opinion about not allowing to modify function arguments except for receivers. This is an idea I got that isn't really implemented in any language I know of.
For example:
mut a := [1, 2, 3]
So instead of
multiply_by_2(&a)
we have to return a new array (and this will later be optimized by the compiler of course)
a = multiply_by_2(a)
I think this will make programming in this language much safer, and the code will be easier to understand, since you can always be sure that values you pass can never be modified.
For some reason all new languages like Go, Rust, Nim, Swift use a lot of mutable args in their stdlibs.
You can still have methods that modify fields, this is not a pure functional language, because it has to be compatible with C/C++:
fn (f mut Foo) inc() {
f.bar++
}
- physguy1123 8y agoI'm not sure if I'm the target audience for this (low-latency trading), but here's my thought - code which would allocate in a fast path is a strict no-go for me, and this runs fairly close to that in a few regards: > It seems easy to accidentally make allocating code allocate by changing some variable names (b = fnc(a) - oops, allocation) > I would be extremely wary of trusting a compiler to realize what sort of allocations are optimizable and not - I already have problems with inlining decisions, poor code layout, etc in fairly optimized c++/rust code compiled by gcc+llvm. Replace allocation with any other expensive work that duplicating a datastructure would result in, and you have the same story. I suspect many of the people that would be evaluating this compared to C/C++/Rust have similar performance concerns.
- amedvednikov 8y agoThanks for your input. I'll do more research on this.
- gcb0 8y agoYou need to stop pretending they are not globals. Just accept you work on one continuous memory patch and are simply passing pointers around. If you don't lie to yourself, you can't shoot your own foot when the compiler do not see your lie.
- nudpiedo 8y agonot all abstractions are leaky, many compilers/language grammars enforce the paradigm of local lexical contexts. Playing a game without accept its self imposed restrictions whose player accepted voluntarily would be lying to oneself.
- skybrian 8y agoIf you can't mutate an argument, does that mean you can't call any of the argument's methods that mutate it, either?
- amedvednikov 8y agoRight.
- jonhohle 8y ago> This is an idea I got that isn't really implemented > in any language I know of. Is this different than an arg declared as a const reference in C++. Or since fields are still mutable, like `final` arguments in Java?
- amedvednikov 8y agoI think yes, it's like declaring every single argument as const.
- bakul 8y agoAlgol68 had this property. Consider STRUCT (INT x, y) z = (1, 2) Here z is a const struct. If you wanted a variable, you'd have to say STRUCT (INT x, y) z; z := (1, 2) or equivalently REF STRUCT (INT x, y) z = LOC STRUCT (INT x, y) := (1, 2) Basically a variable has a REF mode.
- calebwin 8y agoIsn't the main reason why languages like Go and Rust don't do that because it would produce a ton of memory garbage?
- amedvednikov 8y agoIt will be optimized by the compiler.
- nicoburns 8y agoCan it really always be optimised by the compiler. For example, I imagine optimising `sort(&arr)` which cannot mutate arr could be quite difficult, no?
- deleted 8y ago[deleted]
- amedvednikov 8y agoWhat I mean is `a = sort(a)` will be optimized to `sort(&a)` which can mutate internally.
- eridius 8y agoHow? In-place sorts and out-of-place sorts have different implementations.
- strmpnk 8y agoI assume `a` would need to be copied into the local scope of the function and the optimization would be to elide the copy after analysis shows the original a is safely consumed by the call site so it does not require persistence. This probably means lots of aliasing restrictions or in the case where the optimization can't be done, copying could be an expensive side effect of an innocent refactoring. I hear Swift uses something like this, though it's copy-on-write. I've not used Swift in any significant capacity. Does anyone else have experiences to share with this model?
- nicoburns 8y agoI think Rust and Swift's approach of approach of making the user explicitly annotate (both at function definition time and a call time) these kind of parameters works pretty well. I think you're right that it can often be an antipattern. But there are also use cases (usually for performance reasons), and the real problem occurs when you are not expecting the modification to happen. If the parameter is annotated then it's obvious that it might be mutated, and less of an issue... P.S. Looking forward to the open source release. This langiage looks pretty nice/interesting to me, but there's no way I would invest time into learning a closed source lanaguage.
- amedvednikov 8y agoI don't think it's possible to have a closed source language in 2019 :) It will be released with an open source this year.
- User23 8y agoTechnically (and we are technical folks) it's a language with a closed source reference implementation. A serious language designer is going to specify his language, so that other implementations, closed or open source, are possible if not available. C is the ur-example, there are dozens of implementations, some proprietary and some free.
- amedvednikov 8y agoThere's no reference implementation yet. I had a small page about the language up. I don't have some of the most basic things implemented and figured out yet.
- User23 8y agoI’m very happy to see that you’re doing this. Keep at it!
- iainmerrick 8y agoWe aren’t in a phase where languages have multiple implementations right now. I can’t think of any well-known newish language (created in the last 10 years, say) with multiple implementations. Rust, Kotlin, Swift, Julia, Dart... any others? Go might be a counterexample with its cgo implementation, but that was built by the same team and I have the impression (maybe mistaken) that it’s fallen by the wayside. I don’t know if this indicates a really new language development style, with less emphasis on specification, or if it’s just a cyclic thing and some of these new languages will gain more implementations as they get more established.
- rurban 8y agopass by copy is a traditional safe mechanism in functional languages, esp. for multithreading. It can also be made fast enough.
- dnautics 8y agoKeep in mind that many of these functional PLs encourage datatypes like first class linked lists that make this copy semantic very fast.
- henrikeh 8y agoIn Ada, procedure and function arguments must be annotated with whether they are “in”, “out”, “in out”. “In” can never be modified, “out” has no initial value and “in out” has both initial value and can be modified. Overall if you don’t know Ada I’d recommend that you take a look at the features of it. It has very similar design goals as your V.
- milancurcic 8y agoPure functions in Fortran do this.
- petre 8y agoAlso D with the immutable type qualifier. https://dlang.org/spec/function.html#pure-functions https://dlang.org/spec/function.html#pure-functions
- fooker 8y ago> (and this will later be optimized by the compiler of course) Do not underestimate this part. It can take a surprising amount of effort to get these things right and performant.
- GrayShade 8y agohttp://wiki.c2.com/?SufficientlySmartCompiler http://wiki.c2.com/?SufficientlySmartCompiler
- runeks 8y agoI wanted to ask OP about this part specifically, so I’ll write my questions here. a) how do you plan to do this? b) will the optimization still kick in if you name the return value something else than “a”? c) what if “a” is an argument passed to the function that calls “multiplyBy2”? Then doing an in-place update would modify the value of “a” for some other function that has also been passed “a” as an argument.
- robobro 8y agoever studied Limbo? look at Limbo instead of Go.
- register 8y agoThis is exactly what I was looking for. There's no real option for a language that allows interactive coding and it's easily embeddable. Please open source asap. You will get contributors starting with me.
- laumars 8y agoI’m not taking anything away from the goals of this project but I do feel it’s worth mentioning that there actually are lots of languages that offer interactive coding and are embeddable. Eg JavaScript, Python, LISP, Perl, Lua, etc. Heck, even BASIC fits that criteria. I do wish the author the best with this project though. Plus designing your own language is fun :)
- register 8y agoAll of them have shortcomings when one wants multi-threading. Javascript , Python and Lua implementations are single threaded. Perl is slow. LISP is ideal but the commercial distributions with these features have licesing costs of thousand $. The open source (Common Lisp) implementations have all other deficiencies: not easily embeddable and/or big image sizes and/or slow and/or with poor GC. Currently the best open source option seems to be Gambit Scheme. I am playing with it and while the author is extremely supportive some points are still a little bit rough. In the single thread world there is already a clear winner and that is Lua.
- laumars 8y agoAh yes. I understand a little more the context behind your post. And I don't disagree with the points you raised there either. Thank you for replying :)
- lucas_membrane 8y agoAda does not allow functions that alter parameters for their callers (out mode parameters).
- Straw 8y agoAlthough you can certainly go the pure functions route, I wouldn't recommend it for performance. There's a false dichotomy between functions and methods, which are simply (sometimes dynamically dispatched) functions with a special first argument. If you allow mutable first arguments, why not any argument? Instead of the language deciding what's mutable and not, I'd rather have a const system like C/C++ to ensure that changes aren't happening behind the programmer's back.
- coldtea 8y agoHello, this is an excellent language! Have been looking for something like this for a long time! Re: "not allowing to modify function arguments except for receivers" -- maybe instead all fields const by default, but having something like an optional mut modifier? A quick question, how does hot reloading work (with presumably AOT compilation and no runtime)? (Perhaps there's a OS mechanism to change already loaded code in memory that I should know).
- masklinn 8y ago> For some reason all new languages like Go, Rust, Nim, Swift use a lot of mutable args in their stdlibs. Both Rust and Swift require specifically opting into parameter mutation (respectively `&mut`[0] and `inout`) and the caller needs to be aware (by specifically passing in a mutable reference or a reference, respectively), only the receiver is "implicitly" mutable, and even then it needs to be mutably bound (`let mut` and `var` respectively). Inner mutability notwithstanding, neither will allow mutating a parameter without the caller being specifically aware of that possibility. The ability to mutate parameter is useful if not outright essential, especially lower down the stack where you really want to know what actually happens. Haskell can get away with complete userland immutability implicitly optimised to mutations (where that's not observable), Rust not so much. [0] or `mut` in pass-by-value but the caller doesn't care about that: either it doesn't have access to the value anymore, or it has its own copy
- IshKebab 8y agoI think it would be a bit weird if fields of structs can be modified, but bare values can't. Kind of feels like the inconsistency between `Integer` and `int` in Java. So I would say that having to explicitly mark function parameters as mutable (like in Rust) is a better approach.
- whalesalad 8y agoYes. Don’t mutate in place :thumbsup:
- akrymski 8y agoI actually think having different models (functional and imperative) just adds to confusion. I don't think immutability is all that useful personally, unless the language is purely functional to begin with. I'd keep it simple and stick to C as much as possible. Why not go the other way and pass everything as a reference? After all that's what Java does, and that's how you'd pass any struct in C anyway. It's a rare case when I need to forbid the calling function to not modify an argument for whatever reason or because I don't trust it - in that case you can make a copy beforehand or use a const modifier. But in most cases I'd expect functions to modify the memory that I pass in, instead of allocating and returning new structures. Why not have a simple `class` construct as in JavaScript? Keeping functions together in a class is very convenient and means you don't have to pass the struct as the first argument each time. That way `Array` can be a class, and would always be passed by reference. No ambiguity there, class instances are always mutable. Everyone is already familiar with it, it works. A class method can simply map to a global C function: ``` ClassName_MethodName(*self, ...) ``` As an aside, using a syntax that people are already familiar with (and APIs!) would be great, and make something like this instantly usable. JavaScript has a fairly small core API which would be easy to emulate for example.
- boomlinde 8y ago> Why not have a simple `class` construct as in JavaScript? Keeping functions together in a class is very convenient and means you don't have to pass the struct as the first argument each time. This already seems to have a way to associate functions with data structures, in the same way that methods are done in Go, via a "receiver" before the function name Eg. type Something{} fn (self mut Something) method() { ... } and calling the method with an instance x := Something{} x.method()
- js2 8y agoI have a possibly unhealthy obsession with using namedtuples in my Python and so this pattern appears frequently in my code: object = object._replace(foo=1) But I usually encapsulate the _replace call in the class so it's more like: object = object.update_foo(1) I personally find it can make the code easier to understand, like you say, but it seems like you're getting a lot of disagreement from the other comments.
- masklinn 8y ago> I personally find it can make the code easier to understand, like you say, but it seems like you're getting a lot of disagreement from the other comments. The disagreement they're getting is not on the use of immutability and pure transformations, it's on the fantasy that a "sufficiently smart compiler" would be able to optimise this (especially non-trivial versions of this) into mutations under the cover. Furthermore, V is apparently supposed to be a fairly low-level language, if you have to rely on the compiler to perform the optimisation, can't statically assert that it does so[0] and it fails to, that is extremely costly in both "machine" time and "programmer" time (as you'll start wrangling with the compiler's heuristics to finally get what you need). If you want immutability, do it, but do it properly: build the runtime and datastructures[1] and escape hatches which make it cheap and efficient, don't handwave that the compiler will solve the issue for you. [0] and if you are you may be better off just adding mutation to the language already [1] non-trivial immutable data structures are tree-based and translate to a fair number of allocations, you really want an advanced GC
- boomlinde 8y agoI'm thinking that it's easy to make a mistake that would prevent the optimization from happening, so I'd personally much rather be explicit about mutability than betting on having satisfied the optimizer. This looks like a really interesting project, and I look forward to trying it out!
- friday99 8y agoWhat about the case of multiple outputs? It's traditional to have functions that take other mutable arguments to store different auxiliary return values in. So, with this proposal, you couldn't do that and would have to construct random blobs to store all return values and then unpack them.
- masklinn 8y ago… That's a solved problem, the "random blobs" is called a tuple. Or, since the language is inspired by Go, you can have bespoke multiple return values instead of a reified generic structure.