Skip to content

Comment on Show HN: Vivaldi programming language

Comments

This looks really cool! Some random feedback. It's your baby, so feel free to ignore any and all of this you don't agree with.

First off, you did two things right that a lot of hobbyists (including myself, multiple times) do wrong right out of the gate:

1. You wrote some nice, readable introductory docs.

2. You didn't make the language syntactically weird for no good reasons. I can instantly read your code samples and understand them. Yay!

---

There's a good chance you'll end up regretting making strings mutable. It means APIs that receive and store a string have to either copy the parameter (slow, easy to forget) or run the risk of having something that they would be unchanging change under them.

This is a problem with mutability in general, but I think strings hit the pain point most strongly since they're immutable in other languages.

---

You have lots of methods that don't take arguments (size(), start(), end(), etc.). Some kind of getter syntax would be nice to lose the pointless "()".

---

I like your range syntax. I'm using ".." and "..." (like Ruby) in a hobby language of mine[1], and it's definitely not intuitive to many users.

[1]: https://github.com/munificent/wren

---

Your mixture of ":" for functions and control flow but "do ... end" for blocks hurts my soul in some hard to define way. There's nothing intrinsically wrong with it, but I see ":" and expect Python-style indentation then see "end" and get all confused.

It might be nice to try to pick one or the other -- use indentation for blocks, or "end" for functions and control flow.

---

I think you may eventually find the way "self" is dynamically bound to be an annoying frustration. What you have is semantically simple and follows JS and Lua.

But it trips people up when people want to do things like store a reference to a method in a callback and invoke it later. When that helps, you'll have forgotten the old "self" reference.

Of course, solving this is also fairly complex. It usually requires some kind of explicit function/method distinction (makes the language more complex) or an explicit way to close over "self" (easy for the user to forget).

---

"All variables must be declared before use:"

Damn straight. Implicitly declaration gives me hives. :)

---

I like your cond statement, though I've never personally dug the name "cond". The semantics are neat, though.

---

You could get rid of "try" and just allow any block to have a catch clause. That's what Ruby does, and I've always found it elegant.

Not at all— any feedback is appreciated :)

I'm beginning to have doubts about String mutability. Fortunately the only actually mutating method at the moment is append, which is redundant with add but for the mutability, so I might just tear that out.

Required parentheses on functions are pretty unfortunate. I'm mainly trying to avoid the situation in Ruby where trying to access functions as first-class objects requires all sorts of weird syntactic overhead. Possibly I could have some kind of prefix operator, so (say) &obj.method would return the function object, and obj.method would call it?

I agree about the ':' 'do/end' ugliness. Originally blocks were delimited by braces, but I think the Ruby-style delimiters look nicer. Frankly I'd prefer to get rid of the ':', but for single-expression functions that looks pretty awful. I could do Haskell-style '=' (Haskell of course being where the 'function-body-is-single-expression' thing came from), but

    fn foo(x) = do
      ...
    end
is also pretty weird-looking; kind of like Scala's function definitions, which I find really abrasive for reasons I can't fully explain.

Binding 'self' is also basically a compromise. Originally it was stored in the method, but that required copying each type's methods (and each parents' methods) into an object on instantiation, which was a pretty much unacceptable performance hit. My thinking is that, whenever you'd want to pass a method 'obj.method', it's equally possible to pass 'fn (x): obj.method(x)'.

I need to rework the 'try...catch' statements in any case; their current semantics are incredibly horrible (it wraps, then immediately calls, the try body in a temporary lambda). Maybe I will just incorporate them into blocks— thanks for the idea!

Originally it was stored in the method, but that required copying each type's methods (and each parents' methods) into an object on instantiation, which was a pretty much unacceptable performance hit.

I think more a typical solution is to dynamically bind `self` like you do now on normal method calls. But when a reference to a method is stored, you bind `self` to the original receiver at that point in time.

Basically, you treat it like a closure.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.