100 Languages Speedrun: Episode 87: Sidef
Sidef is a prototype programming language, describing itself as inspired by Ruby, Raku, and Julia.
Installation on OSX
It's not that easy to run Sidef. It runs on Perl 5 platform and you can install it through CPAN, but you'll run into serious problems.
Perl version bundled with OSX is not really recommended, so I used brew version. Unfortunately if we do this:
$ brew install perl
$ cpan Sidef
It will install sidef
in ~/.cpan/build/Sidef-3.99-0/bin/sidef
, but it's hardcoded to use system Perl.
So I needed two additional steps. Symlink ~/.cpan/build/Sidef-3.99-0/bin/sidef
to some place in $PATH
like ln -s ~/.cpan/build/Sidef-3.99-0/bin/sidef ~/bin/
.
And edit sidef
program to change its first line from #!/usr/bin/perl
to #!/usr/bin/env perl
so it will use the proper version, not the outdated one from OSX.
After all these steps, we're ready to go.
This is definitely something Sidef should just handle better. Oh and there's no VSCode syntax highlighting for Sidef. Often even very rarely used language have some.
Hello, World!
Hello, World is completely unsurprising. No ugly semicolons here.
#!/usr/bin/env sidef
say "Hello, World!"
$ ./hello.sf
Hello, World!
FizzBuzz
Sidef indeed looks like something between Ruby, Raku, and Julia.
#!/usr/bin/env sidef
(1..100).each { |n|
if (n % 15 == 0) {
say "FizzBuzz"
} elsif (n % 5 == 0) {
say "Buzz"
} elsif (n % 3 == 0) {
say "Fizz"
} else {
say n
}
}
- range
1..100
goes from1
to100
- there are no
...
ranges {|args| ...}
blocks look like Ruby, but they're not used quite in the same wayif
elsif
else
requires()
s and{}
s
Blocks
Ruby blocks have special place in the language, and block argument is a separate thing from normal arguments. Not so in Sidef.
Sidef has very high level of syntax flexibility.
#!/usr/bin/env sidef
func twice(f) {
say "Running it twice:"
f()
f()
}
var hi = { say "Hi!" }
twice(hi)
twice({ say "Hello!" })
twice { say "This does not work!" }
say ""
say "Iteration:"
{ |i| say "Block got: #{i}" }.each(10..12)
{ |i| say "Block got: #{i}" } << 13..14
for 15..16 { |i| say "Block got: #{i}" }
(17..18).each { |i| say "Block got: #{i}" }
{ |i| say "Block got: #{i+19}" } * 2
2.times { |i| say "Block got: #{i+21}" }
say ""
say "Single argument:"
60 |> { |i| say "Block got: #{i}" }
60 |> :inc |> { |i| say "Block got: #{i}" }
60 |> {|i| i + 2} |> { |i| say "Block got: #{i}" }
60 |> {_+3} |> { |i| say "Block got: #{i}" }
60 |> (:add, 4) |> { |i| say "Block got: #{i}" }
{ |i| say "Block got: #{i}" }(69)
$ ./blocks.sf
Running it twice:
Hi!
Hi!
Running it twice:
Hello!
Hello!
Iteration:
Block got: 10
Block got: 11
Block got: 12
Block got: 13
Block got: 14
Block got: 15
Block got: 16
Block got: 17
Block got: 18
Block got: 19
Block got: 20
Block got: 21
Block got: 22
Single argument:
Block got: 60
Block got: 61
Block got: 62
Block got: 63
Block got: 64
Block got: 69
Everything here works except for Ruby-style twice { say "This does not work!" }
which does literally nothing, and I'm not sure why.
There are some debugging tools like -c compile the code into a Perl program
and -D dump the syntax tree of a program
, but the result is not really human readable.
I thought -k keep track of potential unsafe parser interpretations
might say something about it, but that also doesn't say anything.
Having high degree of syntax flexibility is not that important for normal programming, but it's great for DSLs as it makes it easier for DSLs to pick something that works for them.
Especially the |>
code is very cute.
Fibonacci
Sidef supports is cached
for memoization for free, which is a common enough use case that I don't know why more languages don't do this. There are also ways to clear the cache.
#!/usr/bin/env sidef
func fib(n) is cached {
return 1 if (n <= 2)
fib(n - 1) + fib(n - 2)
}
(1..100).each {|n|
say "fib(#{n}) = #{fib(n)}"
}
$ ./fib.sf
fib(1) = 1
fib(2) = 1
fib(3) = 2
...
fib(98) = 135301852344706746049
fib(99) = 218922995834555169026
fib(100) = 354224848179261915075
Operator Precedence
Sidef tries to be too cute with precedence rules.
#!/usr/bin/env sidef
say(2+3*4+5)
say(2 + 3 * 4 + 5)
say(2 + 3*4 + 5)
say(2+3 * 4+5)
Prints 4 different results:
$ ./math.sf
29
25
19
45
Sidef just doesn't have operator precedence, and instead tries to use spacing to determine what happens, in some completely insane way.
Not following standard operator precedence is the worst idea. Smalltalk tried that, and it killed the language. Every Smalltalk successor had to do all the painful things to unwind this stupid idea. And Smalltalk at least had simple consistent rules - operators always applies left to right. Sidef does something insane:
- without spaces, operators apply right to left (
2+3*4+5
is2+(3*(4+5))
) - with spaces, operators apply left to right (
2 + 3 * 4 + 5
is((2+3)*4)+5
) - with some spaces, operators apply left to right when there are spaces, and have extra parentheses in un-spaced group
This disqualifies the language.
Wordle
Here's a Wordle game in Sidef:
#!/usr/bin/env sidef
var words = File.new("wordle-answers-alphabetical.txt").read.split
var word = words.rand
var guess = ""
while (guess != word) {
print "Guess: "
guess = STDIN.readline
if (guess.size != 5) {
say "Only 5 letter words allowed"
next
}
{|i|
if (word[i] == guess[i]) {
print "🟩"
} elsif (word.include(guess[i])) {
print "🟨"
} else {
print "🟥"
}
} * 5
print "\n"
}
It didn't go too bad:
$ ./wordle.sf
Guess: trial
🟥🟥🟥🟨🟥
Guess: maybe
🟥🟨🟥🟥🟩
Guess: snake
🟨🟥🟩🟥🟩
Guess: chase
🟥🟩🟩🟩🟩
Guess: phase
🟩🟩🟩🟩🟩
Should you use Sidef?
No.
I support fun experiments, but Sidef has the absolute worst way of doing something as simple as adding numbers, making it pretty much unusable for anything.
An even bigger problem is what when you write some code, Sidef decides what it means, and half the time it will decide that you meant something else than you did. There's no error messages, no documentation, no syntax debugging tools, the code will just do whatever it feels like. Sometimes code does something else. Very often the code just doesn't do anything, and that's a lot more baffling.
I think to make Sidef usable for even casual play it would need at least:
- fix operator precedence
- have way better error messages
- have some kind of "print how it parsed" option, that adds a lot of
()
s and such to tell you what the hell Sidef thinks you just did - working OSX package
With these issues fixed, maybe Sidef could become something I could recommend playing with for a weekend or two. In its current state, just no.
Code
All code examples for the series will be in this repository.