ERRATA: Many of the examples are more verbose than necessary. This is partially due to my lack of expertise in many of the languages, and partially due to the desire to show equivalent constructs in each of the languages. In many cases, the most concise version of the program in that particular language would negate the need to use some of those constructs.
I am very surprised you didn't do map and fold in Elixir, and instead opted for a more verbose implementation, basically just doing a fold and a map by hand.
At least there was only one list enumeration in the elixir program. 😅 I’m sure other language experts are picking apart the other examples too. Either way it was fun to look at elixir code keep it up @Code to the Moon
For Julia, you could also have replaced the body of the function 'sum_fn' with the line: parse.(Int, splits) |> sum The dot after parse lets the function operate on an array and it pipes the resulting array into the sum function
Thanks, this was great. I love playing with Rust. After years of watching Jon Gjengset's videos I got his book, Rust for Rustaceans, It's so rare to find intermediate to advanced stuff. I learned erlang and Haskell long ago and have started looking at Elexir. I got into Lua to write pluging in Reaper DAW, it 's faster to knock out than C++, and then fell in love with the wonderful Lua metaprogramming. Take care.
Thanks, I really enjoyed making it. I got Rust for Rustaceans recently as well, it's a fantastic book! I didn't realize Reaper supported Lua as well, seems like everyone is fixated on Neovim's support for it these days
Lau is a scripting language... I didn't mind that. It's another level of abstraction. There is a major difference between scripting and programming - one needs a run time, the other doesn't. I think Lua is great as a tool, if your Hong to learn lua, then python, which can be semi-compiled. I find that a language that has support everywhere is easier for me. Rust on everything for example, Python is everywhere... of course the C's are everything, but they are a pain.
Nim is a really awesome language. I started learning it a few months ago but I keep finding cool stuff about this language, like the macro system, so easy to use and powerful to make very cool things with metaprogramming. If you haven't used yet, Nim is worth giving a try.
I think a notable absence is Vale, it's more akin to Rust than any since it has it's own borrow checker and similar performance while claiming to be more concise and flexible.
I agree, Vale looks super interesting! I was actually going to include it in the video, but I ran into a roadblock trying to read stdin to a String. I asked the lead about it on the Vale Discord, and they said it was something they were going to implement. So I figured I'd do a separate video about it - Vale is actually the language I'm hinting about at the very end
@@Axman6 I want to second this. Using "forever $ do …" (or "forever do …" with {-# LANGUAGE BlockArguments #-}) reads very much just as well as "loop { … }" in Rust.
U can improve ur nim code a bit: import strutils, stdutils proc sumFn(theArr: seq[string]): int = theArr.mapIt(it.parseInt) .foldl(a+b) while true: let line = stdin.readline arr = line.split sum = sumFn(arr) echo sum # end of program Note: No need to use different lines to import different modules, nim compiler removes underscores from indentifiers so it is better to use camelCase instead of snake_case, use mapIt to improve readability and for writing concise code, no need to write return or result at the end of function, function will return that value automatically. Multiple let or var or const statements can be combined into one, thus making code more concise.
Julia supports the exact same pipe operator as Elixir! You also don't need the begin\end in the map of sum_fn, but I'm pretty sure you only included that as a demonstration of a begin block. There's also the `eachline()` function, which if not passed any args, creates an iterator over stdin. Thus, you can write `for line in eachline()` rather than `while true; readline()`
Edit: I just realized someone gave this comment before me. @@codetothemoonIn addition you could do: ``` ints = parse.(Int, splits) sum(ints) ``` Instead of having sum_fn at all. Here the . (dot) is shorthand for broadcast, so it just creates a vector of Integers from the get-go.
I'm gearing up for a larger project in Rust, and depending how it goes, I might chose Nim for another medium size but much smaller project. Nim looks like SO much fun!
nice!! yeah I was really impressed with nim. Somebody mentioned in the comments that they were working on a borrow checking option - now that would be a game changer
Idk if anyone has mentioned it, but for elixir, one easier way of running that code could've been using a .exs file (elixir script) instead. And then just do `elixir .exs` to run it. It's pretty nice
As an Elixir expert I deem this code to be not quite idiomatic, but it showcases pattern-match destructuring and recursion which is, IMO, a better show and tell than using the Enum module to map and sum.
Thanks, I'm learning a ton from experts such as yourself in each of these languages. I believe there is representation for every single one in the comments, which is really cool
So young but beautiful lang with lua-like syntax and LuaJIT speed(and even faster). Who knows maybe if Julia may be older NeoVim devs use it instead of LuaJIT...
I’ve chosen Julia to be my general purpose / ML / DS language for the near future. Hope to work with both RUST and JULIA in health informatics. Let’s see what the future will show.
Julia is not just optional type annotation, it is optional type checking. If you anotate a variable with a type you cannot assign it another type, the compiler will throw you an error there.
@@codetothemoon If I am not mistaking the functional programming language OCaml introduced that pipe operator. That was both Elixir and F# have drawn inspiration from. There are very good video series by a professor from the UK on youtube, the series has more than 100 videos and he shows you have to try the code online on the browser, no need to install anything. I got to OCaml when I after falling in loved with F# I was disappointed to find out that F# is roughly totally based on OCml, they look the same when it comes to the functional style, since F# also accept the OOP approach with imperative style. So now I am sticking to OCaml, since it is where F# came partially from. OCml, Haskell, Erlang the Common Lisp are quite a challenge, but these are really interesting programming languages. I am very surprised how many implementation are so concise in these 4 languages.
@@emoutraspalavras-marloncou4459 as someone who is also learning some OCaml and F#, would still say F# is more 'usable' as it can use/be used alongside C# projects, with C# itself bing quite popular; While OCaml is quite niche and honestly just less practical, with it only recently being able to do async and multi-thread without hacks
Great video! I would love to see a video about Haxe. One of my favourite languages, with an incredible balance for utility and performance. It also has quite a mature ecosystem for how unknown it is.
Rust you might want use the Buffer String type - it strips out the /n /r raw data from the keyboard entry, also your parsing() isn't capturing the Return type, which handles errors.
The Julia version could be implemented like this: # No need to write type annotations since at runtime Julia will look at the type of the argument and compile the function for that type sum_fn(splits) = sum(parse(Int, x) for x in splits) # Putting the code inside a function makes more performant because of how the Julia compiler works function main() line = readline() splits = split(line, " ") result = sum_fn(splits) @show result end main()
Yes, I came here to say that, plus it supports the pipe operator too. I actually tend to write my code using a lot of `Base.Iterators.map` and `Base.Iterators.filter` so that they'll be lazy in their execution lol. ```julia parse_Int(x) = parse(Int, x) function main() # Rather than the infinite loop, you can use eachline() to create an iterator of the lines from stdin for line in eachline() line |> split .|> # Broadcast pipe, applies the subsequent function to the elements of the input, rather than the whole input parse_Int |> sum |> println end end main() ```
Thanks for a fun comparison. Note that you do not need to collect the elements of split_whitespace() just to transform them back to an iterator after. Of course the signature of *sum_fn* then becomes slightly longer: fn sum_fn) -> i32 { n.map(|e| e.parse::().unwrap()).sum() } fn main() { loop { let mut buffer = String::new(); stdin().read_line(&mut buffer).unwrap(); println!("{}", sum_fn(buffer.split_whitespace())); } }
I feel the Julia version is unnecessary verbose, one could just write: while true readline() |> split .|> (x -> parse(Int, x)) |> sum |> println end So in Julia this exercise takes only 3 lines of code.
The Julia example is written in a very unJulia way which does not make good use of features of the language. All the reading and summing can be written very concisely: sum(s->parse(Int,s), split(readline(), " ")) or sum(parse.(Int,split(readline(), " "))) or sum(split(readline(), " ") do s parse(Int,s) end You can divide it somehow to make it more readable, but the version presented for me seem to only make the language look more ugly and confusing.
Nice intro -- it's great to see really nice lesser-known languages like Nim and Crystal get more of a spotlight. BTW, for this type of program in Haskell you can sometimes use "interact", although it can behave surprisingly due to laziness, so I had to use lines / unlines in this example: sum_fn = sum . map (\s -> read s :: Integer) main = interact $ unlines . map (show . sum_fn . words) . lines
Swift implementation, with error handling :) while true { print("input: ", terminator: "") print((readLine() ?? "0") .split(separator: " ") .compactMap { Int($0) } .reduce(0, +) ) } It's a really cool language, has extremely good C and C++ interoperability too :) It's often seen as "the Apple language" sadly, but just ignore the obj-c classes and use structs, it's even adding Rust like ownership soon
I won't even lie to you, I didn't know Swift worked on anything but Apple products until this comment. Guess it's in the same boat as C# now in the sense that it was originally for a proprietary system but expanded compatibility. Interesting.
@@v01d_r34l1ty Yeah, the connection to Apple makes the language look bad; actual Swift code looks completely different from the decades old object oriented apis, but that's what most people will see when they look up Swift on google or UA-cam. It also looks really high level, so at first I assumed it must be slower than Rust; there's no semicolons after all 🙂
@@codetothemoon Yes, this is my takeaway from learning and using F#. It is a very underrated language and popular languages are not always the best ones.
For the Haskell solution, you can also write sum_fn like this: sumFn = sum . map (\s -> read s :: Integer) This is function composition and eliminates the need to write the argument that is being taken.
Elixir has loops and reduce (foldr). It does not need to use recursion. E.g you could use: def sum_fn(strs) do List.foldl(strs, 0, fn s, acc -> String.to_integer(s) + acc end) end or def sum_fn(strs) do Enum.reduce(strs, 0, fn s, acc -> String.to_integer(s) + acc end) end
thanks for pointing this out, this is much cleaner! I've been learning a ton from the reactions of the experts in each language showcased in this video.
nice! I agree the comments are really insightful - I think I've heard from at least one expert in each of the languages covered in the video which is amazing. Also many point out tech that I hadn't heard of before, this one is a good example. I've put Pharo on my "to look into" list!
Actually ORC, one of Nim's memory management strategies, is memory safe without a garbage collector, it has a deterministic performance due to its nature, so it can work like Rust if you annotate your data structures as acyclic and don't mess up (if you mess up, the compiler identifies that and insert cycle detection code to free the memory when needed)
Reference counting IS garbage collection (even if very lightweight), that's what the "RC" in ORC stands for. Cycle collection is also garbage collection, which is what the "O" in ORC represents. Not to say that there is anything wrong with it, Nim is wonderful and so is ORC. ORC is great because it allows you to prototype your code very easily without being forced to think about memory, you can always come back and optimize later (which IMO is the optimal workflow), you can still use memory pools or whatever memory management scheme you would want to use in Rust, you are just not required to worry about such things from the get-go and just leave it to the GC until you require better performance.
9:40 You declared yourself at the beginning of the video, that Nim has reference counting and manual memory management By the way: There you said actually something that could be considered different as you intended You said: Nim has manual memory management - like C. MM in Nim is actually a lot more concise and simple than in C I know you meant its manual in both ways, and this is true Its just not manual in the same way ;) P.S: Nim has also a borrow checker in the works
other nim version import std/strutils import std/sugar # Syntactic sugar for several things, in this case anonymous functions "=>" import std/sequtils proc sum_fn(the_arr:seq[string]): int = the_arr .map(x => x.parseInt) .foldl(a+b) while true: stdin .readline .split .sum_fn .echo in nim, sum(mult(x)) is the same that x.mult().sum() or whitout paramters x.mult.sum
Haskell has the pipe operator too :) It's called (&) and it's in Data.Function. Because Haskell lets you define your own operators, it's not built-in syntax, and you could define it yourself. You can even write `(|>) = (&)` to get the same syntax as elixir. It passes the LHS as to the RHS as its last parameter, instead of its first parameter. (Although of course all functions in Haskell only take one parameter because of currying).
You should've covered Clojure, or any flavor of lisp tbh. Picking a slightly more interesting program next time would be much appreciated. The quicksort algorithm, for example, can have vastly different implementations depending on the paradigm. That said, thanks for the video, really enjoyed the comparisons.
Just a heads up that Elixir's pipe operator is from F# and that Julia has it as well My overall experience from delving deeper into the languages mentioned here is: 1) Julia, Elixir, and Crystal are both incredibly beautiful and well designed languages that deserve more attention, and by this I do not just mean having a short basic syntax but that their underlying semantics are really well designed. Also they have a great macro system. 2) Nim and especially Zig are incredibly disappointing and trying to use them without gc is a royal road to segfaults. With that said, Zig is easier to write than C or unsafe Rust in the sense that its compiler has less UB, but that also means the compiler is less free to optimize code. 3) Haskell is great, but apart from maybe being a bit further from mainstream languages, I really wish its standard library could be rewritten from scratch with the benefit of hindsight and new language features, and that texts teaching it took algorithms seriously and made data structures like finger trees more prominent 4) Lua could have been the perfect scripting language in the 90s instead of python/ruby/js/php, if its standard library was more batteries-included and if netscape could have picked it.
Interesting insights. I used to use Elixir quite a lot and really liked it, although it's not the most performant language for compute-heavy work. We used it to replace a Python-based image-processing system in my old job and it not only produced a MUCH more efficient system, but we ended up with far less code to the same job better and with fewer bugs (immutability by default helped here too). I've used Haskell and Nim for fun, and did pretty much all of the 2020 and 2021 Advent of Code challenges in Haskell, and all the 2022 challenges in Nim (and a few in Crystal too). I'd have to disagree with your second point and say that Nim rarely got in my way or crashed, except when I tried to do some invalid casting of bytes to UTF-8, which did produce an unpleasant segfault. Apart from that it's been great. That said, I wasn't using it without a GC, and relied upon the newish "orc" system which is quite frugal. I really enjoyed working with Haskell too, although the purity of the type system started to get kinda heavy when I wanted to use dynamic programming and other mutation-heavy coding styles. Using mutable arrays with the ST monad was okay but occasionally confusing and very difficult to debug (you can't just throw in a print statement anywhere), and there doesn't seem to be a mutable hashmap type in the stdlib, so I had to do weird things like manually mapping keys into an integer space and using a vector/array instead.
I should have qualified that statement a bit more - all of its mechanisms for memory safety involve some kind of runtime overhead - ie reference counting or garbage collection
I would also add clojure for how short code can look, also clojure has very interisting way oh hadling/embracing null/nil intead of avoiding it. (->> (s/split (read-line) #",") (map #(Integer/parseInt %)) (apply +) (println))
julia is the goat of "interpreted" languages. it combines best features of python+numpy (arrays, broadcasting, list comprehensions, duck-typing) R (big default namespace, lots of numeric functions in the base library) and more low-level languages (structs, higher order function & closure inlining, immutable data, isolated scopes that return values). and it will likely get an efficient compiler for standalone executables!
Do Nim's ARC/ORC memory models compete with Rust's memory safety? My understanding is during compile time it will automatically insert memory de-allocations similar to how Rust does it.
I'm definitely not a nim expert - but my understanding was that you can opt-in to reference counting or garbage collection if you want to guarantee memory safety, but if you opt out of that, you incur the same memory management risks that you would have with C/C++. Somebody jump in here if this is wrong!
@@codetothemoon It is not "safe" as Rust, but ARC/ORC are deterministic memory handling as C++ but with some memory handling done automatically for you, like you'll do with shared_ptr in C++
1:10 This honestly really bothers me. The #1 thing I want is (A) There is only exactly 1 way to do things, (B) There are as few languages syntax rules and keywords to memorize. "loop" violates both. "while True" is definitely superior imo.
Zig solution in 15 lines. Use anytype for parameter type inference in sum_nums declaration. The one line while loop in sum_nums is idiomatic (as long as it isn't more than 80 chars): const std = @import("std"); fn sum_nums(it: anytype) !i32 { var sum: i32 = 0; while (it.next()) |string| sum += try std.fmt.parseInt(i32, string, 10); return sum; } pub fn main() !void { var buf: [10]u8 = undefined; while (try io.getStdIn().reader().readUntilDelimiterOrEof(buf[0..], ' ')) |user_input| { var splits = std.mem.split(u8, user_input, " "); std.debug.print("{} ", .{try sum_nums(&splits)}); } }
actually already did! if you want the original version it's here: ua-cam.com/video/jr1EBaLkjfc/v-deo.html BUT if you want the version with Primeagen commentary (recommended) you can find that here: ua-cam.com/video/j47Hk5qE9As/v-deo.html
More concise Crystal version: def get_sum(splits) splits.map(&.to_i).sum end loop do gets.try do |line| puts get_sum(line.split) end end You don't even need the get_sum method: loop do gets.try do |line| puts line.split.map(&.to_i).sum end end Overall, nice video.
@@codetothemoon Great question. So off the top, coming in hot. - Simple syntax: Zig has a simple syntax comparable to Go, maybe even easier due to it's clever error handling and that it launched with generics. - No garbabe collector: The issues with Nim as much of the standard library is already relient on the GC. - Easier to code in the Rust, my god, Rust is amazing but imagine coupling the safety of Rust with a Golang Syntax = Zig. - Complete compatibity with C libs and C++ - The cross compilation is super fast and is being used by other languages to hit those targets, other languages are leveraging Zig already for compliation work. - C speed, and faster than C with better compiler. Basically if your a C developer you're being advised to use Zig to compile your C code. Zig will replace C, that's without a doubt. I even speculate it replacing Golang down the line as the memory management is easy. So why comprimise on speed when you don't need to. Zig has about two years of catch up planned to reach 1.0 and for editor tooling to evolve.
Julia also has Elixir's |> operator, it does the same thing! On behalf of all Julians I demand that the Elixirites share their trophy with us... or give us a copy of the trophy (we respect that functional programmers dislike shared state)
If you really want to see this problem solved concisely, you should look at APL. I’m not sure if my implementation is correct (I am a beginner at APL and I don’t have an interpreter available right now) but it should be something like the following: {⎕←+/⍎v ←⎕ ⋄ ∇} The braces denote an anonymous function, and ⋄ separates statements. The first statement does IO and all the logic, and the second one is the recursion. It should be possible to do be even more concise (without an anonymous function) but my APL is not good enough to know how to do it.
It's even easier than you think, but kudos for learning. Try this +/⎕ ⎕ (quad) by itself means read a line from standard input and evaluate as an APL expression. So if you type in a bunch of integers it'll turn it into an integer array. Then obviously +/ just sums it up and outputs.
the haskell implementation would look nicer by using the `interact :: (String -> String) -> IO ()` function as well as using the point free style: `main = interact (print . sum . map read . words) >> main`. I'm surprised your editor didn't say "Hey, eta reduce!"
Good idea, though remember that interact want (String -> String) so no print in your function. You also don't need to recurse, if you want to do "f" line by line, you can always use `interact (unlines . map f . lines)`. Here, with TypeApplications to specify the Read type elegantly, you can do this : interact $ unlines . map (show . sum . map (read @Integer) . words) . lines
All these languages are just a little collaj of existing language features. Rust is just new. The borrow checker is a completely new feature and it is the best way to statically manage memory.
@@codetothemoon Oh, that's great. I would like to use this opportunity to ask some questions. 1. You declared the buffer as mutable. So the compiler can understands that buffer is mutable. But why we need to use &mut with buffer in next line ? Didn't the compiler already understand that? 2. What does unwrap() does ?
@@kcvinu 1. Rust is sometimes quite explicit not because the compiler couldn't understand it but to have it explicitly mentioned in the code for the human reader. Something with references. In C++ you define whether something is passed by reference in the function header and you don't see it in the call at all. So it's more like: This is important information for the reader of the calling code / code that uses a function, so it has to be given explicitly. Rust is not very explicit in other cases (type inference is heavily used) Edit: That's not a Rust invention, if I remember correctly C# is also already more explicit than C/C++ with these things) Edit2: You could also immutably pass a mutable String to a function. So the let mut buffer says it can be mutated (in general) and the read_line(&mut buffer) says it can mutated within read_line. 2. There are two things, which are very commonly used in Rust: Option and Result. Option is used when you expect a value, but it could also be that there is no value - the "no-value" is called None. It's basically the replacement for Null. Result is you expect a result (from a function) but it could be that you get an error. It's Rust's error handling (instead of try..catch or the different variants of error return values in C). What both have in common is, that you Rust forces you to deal with the "extra" cases (None, Error). The "dirty" way is using unwrap(), which says: I know what I'm doing, there is a value inside this Option/Result, just unwrap it and give it to me. If you're wrong and there is a None or an Error the program will crash.
Interesting video... but I like the end "Has my love for rust waned? It hasn't" Very good point. Other languages can have maybe better syntax changes or some ergonomics but that's not worth losing memory safety with no garbage collection. If some other language tried to implement memory safety with no garbage collection with better ergonomics... Rust would probably just adopt those ergonomics too because it's constantly improving and gaining ergonomics. It's hard to even fathom something better unless there's some massive rift/scandal in the main contributors and people who manage and use the language which would be surprising because the community is so good.
I used haskell in a universaty course about programming languages paradigms and general stuff. I haven't used in since, but I really do remember how beautiful and concise the code was
yeah it sure is - i had to use ocaml for a course in university, and I remember being really frustrated with it. I was too shortsighted to appreciate the beautify of it back then, but I sure do now
In Haskell instead of using map with a lambda you can use list comprehension. It's significantly more readable imo sum_fn ws = sum $ map (\s -> read s :: Integer) ws sum_fn ws = sum [read s :: Integer | s > main
After seeing Haskell's `interact` mentioned in the comments, I figured out how to make the Idris version even sleeker: import System.REPL import Data.String main : IO () main = repl " " (cast . sum . map cast . words)
Recursion is GREAT if you know what you're doing, it just requires different syntax and can cause memory uncrease (although I like minimizing vars in functions anyway \_[-_-]_/).
I don't care about how many people tell me Nim is good... I hate Nim! Also, you shouldn't be able to change how the core language works. That sounds like a nightmare.
I wanted to use Crystal for a long time but Windows support was no good at the time. I ended up downloading and installing V, it was easy and quickly to get going, I learnt most of the basic syntax in less than a week. Top G Video!
V has a long history of overpromising and underdelivering. Like promising Rust-like automatic memory management without lifetime annotations and GC and ending up with just leaking memory and using a conservative GC. V is for vapourware.
nice, that is much more concise. Many (most?) of the solutions turned out more verbose than necessary. I really enjoy seeing the more optimal versions from those that are more proficient in each respective language
5:35 If you like that feature, you might love FSharp, who come up with this operator and is very cute overall Like Elixir, just statically typed with the most accurate type inference you had ever seen And dotnet as a platform
it sounds like you do qualify as a language nerd, and I'd see that as a good thing! 😎 re: Haxe - I just checked my "list of tech to look into" and it was already on there! Would you recommend learning Ocaml first or going straight to Haxe?
@@codetothemoon ocaml is an awesome language, but I think that Haxe is a lot easier to pick, and has the impressive advantage of being designed to compile to other languages (a lot of them). Also, it has a lot of features that will remind you the best of rust (like static extensions )
@@codetothemoon if you want some example usages beyond games, I have two repositories. One for creating neovim plugins (using Haxe instead of Lua) and another one for creatingg hammerspoon spoons (again, Haxe compiling to Lua)
2:16 Nim - ooh, it appears to fix my biggest complaint with Python when programs get larger, that there is no compiler checking of function parameters and interfaces, and that the hints are just ignored! Forget the extra external tooling/linting pass. 3:37 Julia - nice, visible control structure with symmetric open/close. Something about Python and YAML feels so asymmetric, with the block just dangling in the air and no clear closure, unlike Java/JSON/XML. 6:04 Nim - sigh, yet another language that evidently hates vector and matrix math. If you don't support basic operators on vectors and matrices, then as a language you're getting dumped into my garbage bin, because I do way to much graphics programming to be calling v.multiply(w).add(t) (reminiscent of Java) instead of just v * w + t. 6:32 Nim - though, I do find the use of 'const' for constants and 'var' for variables just lovely, compared to 'let' for constants and 'let mut' for variables (ugh).
About your 3 comment, Nim does support operator overloading and custom operator (including utf8). You just have to declare a function like proc `+`(mat1, mat2: matrix): matrix= //Code And yeah, having to write let for each variable is a bit annoying by time but Nim have let blocks for declaring a bunch of variables at onces, example: let X = 5 // Nim will infer the type of the expression Y: int // you don't need to set a default value X: seq[int]// you can do any type in a let block You also have var and const blocks
How to get the |> operator in Haskell: infixl 1 |> x |> f = f x Alternatively, it is already in the base library, you just have to import it. It's called & but you can alias it to |> if you want import Data.Function ((&)) (|>) = (&)
I'm not doing anything special, I just uncommented the relevant programming languages in init.el and switched to the monokai-pro theme. The only real config I had to add was some stuff to get Zig working, since I don't think DOOM supports it out of the box.
There is 1 language.. i dont personally use it but i want to... AS3 (ActionScript 3)... it was used alot but is less used because of flash dieing (ruffle flash is bringing it back)... to be honest... AS3 is over looked alotl
Hey, would have been cool to actually show comparison between 'business' LOC and ones that glue it in-between. I never actually expected zig to be so cumbersome. Anyways here is the same thing in C#, I think it's a mix of both worlds in terms of expressiveness and fast-ness Console.WriteLine(Console.ReadLine().Split(' ').Sum(int.Parse));
Would you be interested in that? I'd love to make one, I've been unsure about whether folks find editor related videos valuable - especially because I believe most folks watching are using VSCode. Also I'm still on the fence on whether to move forward with Doom or Neovim...
ERRATA: Many of the examples are more verbose than necessary. This is partially due to my lack of expertise in many of the languages, and partially due to the desire to show equivalent constructs in each of the languages. In many cases, the most concise version of the program in that particular language would negate the need to use some of those constructs.
do you know if maybe someone has rewritten the Zig example more idiomatically and succinctly? I'd love to see that.
0:53 Rust
2:04 Nim
3:14 Julia
3:58 Elixir
4:01 - 5:05 sponsor break: Sidekick
5:06 Elixir resume
5:50 Zig
7:06 Haskal
8:02 Lua
8:35 Crystal
*Haskell
I am very surprised you didn't do map and fold in Elixir, and instead opted for a more verbose implementation, basically just doing a fold and a map by hand.
Yeah, figuring out all the fun and funky things one can do with fold is half the fun of functional programming.
Thanks for pointing this out! Sadly, I don't think the Elixir implementation was the only one that was more verbose than necessary...
@@codetothemoon you might wanna redo this video then
@@zzzyyyxxx A followup rather than a redo would be of interest.
At least there was only one list enumeration in the elixir program. 😅 I’m sure other language experts are picking apart the other examples too. Either way it was fun to look at elixir code keep it up @Code to the Moon
Your local Crystal fan here. Not only does it have a really comfy syntax, but the standard library is insane. More people should give Crystal a try!
Nice! Crystal is definitely one of the languages I was most impressed with when preparing this video.
yeah man been using crystal for quite some time and kinda gotten addicted... wait do you mean the programming language? /s
Late reply, but what are the main applications for Crystal? Does it have good documentation?
For Julia, you could also have replaced the body of the function 'sum_fn' with the line:
parse.(Int, splits) |> sum
The dot after parse lets the function operate on an array and it pipes the resulting array into the sum function
thanks for pointing this out! several of the solutions in this video are suboptimal, Julia seems to be one of the more egregious cases of that
@@codetothemoon I would also use Vector{
Ahh .. more language syntax that I have no idea what it does.
Thanks, this was great. I love playing with Rust. After years of watching Jon Gjengset's videos I got his book, Rust for Rustaceans, It's so rare to find intermediate to advanced stuff. I learned erlang and Haskell long ago and have started looking at Elexir. I got into Lua to write pluging in Reaper DAW, it 's faster to knock out than C++, and then fell in love with the wonderful Lua metaprogramming. Take care.
Thanks, I really enjoyed making it. I got Rust for Rustaceans recently as well, it's a fantastic book! I didn't realize Reaper supported Lua as well, seems like everyone is fixated on Neovim's support for it these days
Reaper is a great DAW, love it!
lua 'end syntax' and 1 based indexing is annoying to me
Lau is a scripting language... I didn't mind that. It's another level of abstraction. There is a major difference between scripting and programming - one needs a run time, the other doesn't. I think Lua is great as a tool, if your Hong to learn lua, then python, which can be semi-compiled.
I find that a language that has support everywhere is easier for me. Rust on everything for example, Python is everywhere... of course the C's are everything, but they are a pain.
Nim is a really awesome language. I started learning it a few months ago but I keep finding cool stuff about this language, like the macro system, so easy to use and powerful to make very cool things with metaprogramming. If you haven't used yet, Nim is worth giving a try.
I was impressed by nim as well! Seems like it has a bright future
check "sugar" module!
@@agustinpizarro That module always blow my mind!!! 🤯
Meta programming is above my mental ability. I had some excitement for Nim last year, nothing came off it.
I just started learning nim. Been a fun experience so far.
I think a notable absence is Vale, it's more akin to Rust than any since it has it's own borrow checker and similar performance while claiming to be more concise and flexible.
I agree, Vale looks super interesting! I was actually going to include it in the video, but I ran into a roadblock trying to read stdin to a String. I asked the lead about it on the Vale Discord, and they said it was something they were going to implement. So I figured I'd do a separate video about it - Vale is actually the language I'm hinting about at the very end
Haskell can be even more concise, but no need to (in production code).
main = getLine >>= print . sum . map read . words >> main
or use interact
wow, nice! thanks for pointing this out!
With interact:
main = interact $ unlines . map (show . sum . map read . words) . lines
This is longer but I think more understandable.
@@codetothemoon the >> and >>= are Haskell's monad binding operators, and from my research it is quite similar to elixir's pipe as well
You can also just use forever instead of the explicit recursion.
@@Axman6 I want to second this. Using "forever $ do …" (or "forever do …" with {-# LANGUAGE BlockArguments #-}) reads very much just as well as "loop { … }" in Rust.
U can improve ur nim code a bit:
import strutils, stdutils
proc sumFn(theArr: seq[string]): int =
theArr.mapIt(it.parseInt)
.foldl(a+b)
while true:
let
line = stdin.readline
arr = line.split
sum = sumFn(arr)
echo sum
# end of program
Note: No need to use different lines to import different modules, nim compiler removes underscores from indentifiers so it is better to use camelCase instead of snake_case, use mapIt to improve readability and for writing concise code, no need to write return or result at the end of function, function will return that value automatically. Multiple let or var or const statements can be combined into one, thus making code more concise.
Julia supports the exact same pipe operator as Elixir! You also don't need the begin\end in the map of sum_fn, but I'm pretty sure you only included that as a demonstration of a begin block. There's also the `eachline()` function, which if not passed any args, creates an iterator over stdin. Thus, you can write `for line in eachline()` rather than `while true; readline()`
oh nice I didn't realize this! I'm learning a ton hearing from practitioners of all of these languages - thanks for pointing these things out!
Edit: I just realized someone gave this comment before me.
@@codetothemoonIn addition you could do:
```
ints = parse.(Int, splits)
sum(ints)
```
Instead of having sum_fn at all. Here the . (dot) is shorthand for broadcast, so it just creates a vector of Integers from the get-go.
I'm gearing up for a larger project in Rust, and depending how it goes, I might chose Nim for another medium size but much smaller project. Nim looks like SO much fun!
nice!! yeah I was really impressed with nim. Somebody mentioned in the comments that they were working on a borrow checking option - now that would be a game changer
Idk if anyone has mentioned it, but for elixir, one easier way of running that code could've been using a .exs file (elixir script) instead. And then just do `elixir .exs` to run it. It's pretty nice
As an Elixir expert I deem this code to be not quite idiomatic, but it showcases pattern-match destructuring and recursion which is, IMO, a better show and tell than using the Enum module to map and sum.
Thanks, I'm learning a ton from experts such as yourself in each of these languages. I believe there is representation for every single one in the comments, which is really cool
Yay, somebody finally mentioning Julia! Might be the best kept secret in all of programming.
There is no such thing. If something is good in programming, it won't stay a secret.
So young but beautiful lang with lua-like syntax and LuaJIT speed(and even faster). Who knows maybe if Julia may be older NeoVim devs use it instead of LuaJIT...
It does seem under-appreciated!
I have such a love-hate relationship with julia. But it is becoming my go-to language, so it is doing something right 😅
I’ve chosen Julia to be my general purpose / ML / DS language for the near future. Hope to work with both RUST and JULIA in health informatics. Let’s see what the future will show.
Julia is not just optional type annotation, it is optional type checking. If you anotate a variable with a type you cannot assign it another type, the compiler will throw you an error there.
thanks for pointing this out! the distinction is subtle but probably important to be aware of
Would love to see a follow-up with idiomatic code from experts of these languages!
The pipe operator also appears in F# and it's such an amazing language feature. I miss it.
Ahh I didn't know F# had it too, I do hope it becomes more commonly implemented in new languages
♥|> F#
@@codetothemoon believe it or not but c++ 20 has it 😂
@@codetothemoon If I am not mistaking the functional programming language OCaml introduced that pipe operator. That was both Elixir and F# have drawn inspiration from. There are very good video series by a professor from the UK on youtube, the series has more than 100 videos and he shows you have to try the code online on the browser, no need to install anything. I got to OCaml when I after falling in loved with F# I was disappointed to find out that F# is roughly totally based on OCml, they look the same when it comes to the functional style, since F# also accept the OOP approach with imperative style. So now I am sticking to OCaml, since it is where F# came partially from. OCml, Haskell, Erlang the Common Lisp are quite a challenge, but these are really interesting programming languages. I am very surprised how many implementation are so concise in these 4 languages.
@@emoutraspalavras-marloncou4459 as someone who is also learning some OCaml and F#, would still say F# is more 'usable' as it can use/be used alongside C# projects, with C# itself bing quite popular; While OCaml is quite niche and honestly just less practical, with it only recently being able to do async and multi-thread without hacks
Great video! I would love to see a video about Haxe. One of my favourite languages, with an incredible balance for utility and performance. It also has quite a mature ecosystem for how unknown it is.
Thanks, I've put Haxe on my "to check out" list - which is swelling in size quite rapidly (I see that as a good thing)!
Rust you might want use the Buffer String type - it strips out the /n /r raw data from the keyboard entry, also your parsing() isn't capturing the Return type, which handles errors.
The Julia version could be implemented like this:
# No need to write type annotations since at runtime Julia will look at the type of the argument and compile the function for that type
sum_fn(splits) = sum(parse(Int, x) for x in splits)
# Putting the code inside a function makes more performant because of how the Julia compiler works
function main()
line = readline()
splits = split(line, " ")
result = sum_fn(splits)
@show result
end
main()
Also wanted to add that Julia has the pipe operator too, not only Elixir
Yes, I came here to say that, plus it supports the pipe operator too. I actually tend to write my code using a lot of `Base.Iterators.map` and `Base.Iterators.filter` so that they'll be lazy in their execution lol.
```julia
parse_Int(x) = parse(Int, x)
function main()
# Rather than the infinite loop, you can use eachline() to create an iterator of the lines from stdin
for line in eachline()
line |>
split .|> # Broadcast pipe, applies the subsequent function to the elements of the input, rather than the whole input
parse_Int |>
sum |>
println
end
end
main()
```
Thanks for a fun comparison. Note that you do not need to collect the elements of split_whitespace() just to transform them back to an iterator after. Of course the signature of *sum_fn* then becomes slightly longer:
fn sum_fn) -> i32 {
n.map(|e| e.parse::().unwrap()).sum()
}
fn main() {
loop {
let mut buffer = String::new();
stdin().read_line(&mut buffer).unwrap();
println!("{}", sum_fn(buffer.split_whitespace()));
}
}
Nice, thanks for pointing this out!
I feel the Julia version is unnecessary verbose, one could just write:
while true
readline() |> split .|> (x -> parse(Int, x)) |> sum |> println
end
So in Julia this exercise takes only 3 lines of code.
The Julia example is written in a very unJulia way which does not make good use of features of the language. All the reading and summing can be written very concisely:
sum(s->parse(Int,s), split(readline(), " "))
or
sum(parse.(Int,split(readline(), " ")))
or
sum(split(readline(), " ") do s
parse(Int,s)
end
You can divide it somehow to make it more readable, but the version presented for me seem to only make the language look more ugly and confusing.
Julia is underrated
I'd have to agree!
Nice, For people who are interested in data science, Julia is a great language to explore! I moved part of my workflow from Python to Julia.
I was indeed pretty impressed with Julia. With Mojo coming along, it'll be interesting to see how the landscape evolves in the coming years
Nice intro -- it's great to see really nice lesser-known languages like Nim and Crystal get more of a spotlight. BTW, for this type of program in Haskell you can sometimes use "interact", although it can behave surprisingly due to laziness, so I had to use lines / unlines in this example:
sum_fn = sum . map (\s -> read s :: Integer)
main = interact $ unlines . map (show . sum_fn . words) . lines
Swift implementation, with error handling :)
while true {
print("input: ", terminator: "")
print((readLine() ?? "0")
.split(separator: " ")
.compactMap { Int($0) }
.reduce(0, +)
)
}
It's a really cool language, has extremely good C and C++ interoperability too :)
It's often seen as "the Apple language" sadly, but just ignore the obj-c classes and use structs, it's even adding Rust like ownership soon
I won't even lie to you, I didn't know Swift worked on anything but Apple products until this comment. Guess it's in the same boat as C# now in the sense that it was originally for a proprietary system but expanded compatibility. Interesting.
@@v01d_r34l1ty Yeah, the connection to Apple makes the language look bad; actual Swift code looks completely different from the decades old object oriented apis, but that's what most people will see when they look up Swift on google or UA-cam.
It also looks really high level, so at first I assumed it must be slower than Rust; there's no semicolons after all 🙂
Crystal is the one I was most interested in here. Thanks for informing me about it. I will be checking it out
nice, yeah I was really impressed with Crystal. Definitely worth a look imo!
If you like the pipe operator in Elixir, you might also like F#
oh nice I'd like to check it out! definitely heard F# mentioned in the context of underrated/overlooked languages....
As of today, one language should be very special to make me ever consider using it. F# is awesome!
@@codetothemoon Yes, this is my takeaway from learning and using F#. It is a very underrated language and popular languages are not always the best ones.
Crystal program can be made even more compact.
loop do
line = gets || "0"
puts line.split.map(&.to_i).sum
end
Thanks!
thank you so much for your support!!!
For the Haskell solution, you can also write sum_fn like this:
sumFn = sum . map (\s -> read s :: Integer)
This is function composition and eliminates the need to write the argument that is being taken.
sum . map (read @Integer)
The whole app can just be:
main = forever (print @Integer . sum . map read . words =
Elixir has loops and reduce (foldr). It does not need to use recursion. E.g you could use:
def sum_fn(strs) do
List.foldl(strs, 0, fn s, acc -> String.to_integer(s) + acc end)
end
or
def sum_fn(strs) do
Enum.reduce(strs, 0, fn s, acc -> String.to_integer(s) + acc end)
end
thanks for pointing this out, this is much cleaner! I've been learning a ton from the reactions of the experts in each language showcased in this video.
Zig? I keep learning even when I don't expect to.
And btw the comments are also interesting.
Oh yeah, there's also Pharo?
Great post.
nice! I agree the comments are really insightful - I think I've heard from at least one expert in each of the languages covered in the video which is amazing. Also many point out tech that I hadn't heard of before, this one is a good example. I've put Pharo on my "to look into" list!
Actually ORC, one of Nim's memory management strategies, is memory safe without a garbage collector, it has a deterministic performance due to its nature, so it can work like Rust if you annotate your data structures as acyclic and don't mess up (if you mess up, the compiler identifies that and insert cycle detection code to free the memory when needed)
In this documentation I see "ORC" referred to as a "full blown garbage collector" nim-lang.org/blog/2020/10/15/introduction-to-arc-orc-in-nim.html
Reference counting IS garbage collection (even if very lightweight), that's what the "RC" in ORC stands for. Cycle collection is also garbage collection, which is what the "O" in ORC represents. Not to say that there is anything wrong with it, Nim is wonderful and so is ORC. ORC is great because it allows you to prototype your code very easily without being forced to think about memory, you can always come back and optimize later (which IMO is the optimal workflow), you can still use memory pools or whatever memory management scheme you would want to use in Rust, you are just not required to worry about such things from the get-go and just leave it to the GC until you require better performance.
9:40 You declared yourself at the beginning of the video, that Nim has reference counting and manual memory management
By the way: There you said actually something that could be considered different as you intended
You said: Nim has manual memory management - like C.
MM in Nim is actually a lot more concise and simple than in C
I know you meant its manual in both ways, and this is true
Its just not manual in the same way ;)
P.S: Nim has also a borrow checker in the works
Julia also has an excellent pipe operator !
other nim version
import std/strutils
import std/sugar # Syntactic sugar for several things, in this case anonymous functions "=>"
import std/sequtils
proc sum_fn(the_arr:seq[string]): int =
the_arr
.map(x => x.parseInt)
.foldl(a+b)
while true:
stdin
.readline
.split
.sum_fn
.echo
in nim, sum(mult(x)) is the same that x.mult().sum() or whitout paramters x.mult.sum
This should be made into a repo and take in PRs so we get the best of the best in each language
it's all here! will gladly accept PRs. github.com/Me163/youtube/tree/main/RustVsSevenLanguages
Haskell has the pipe operator too :)
It's called (&) and it's in Data.Function.
Because Haskell lets you define your own operators, it's not built-in syntax, and you could define it yourself.
You can even write `(|>) = (&)` to get the same syntax as elixir.
It passes the LHS as to the RHS as its last parameter, instead of its first parameter.
(Although of course all functions in Haskell only take one parameter because of currying).
ahh thanks for pointing this out!
You should've covered Clojure, or any flavor of lisp tbh. Picking a slightly more interesting program next time would be much appreciated. The quicksort algorithm, for example, can have vastly different implementations depending on the paradigm. That said, thanks for the video, really enjoyed the comparisons.
Thanks for the feedback, maybe in the next one!
Just a heads up that Elixir's pipe operator is from F# and that Julia has it as well
My overall experience from delving deeper into the languages mentioned here is:
1) Julia, Elixir, and Crystal are both incredibly beautiful and well designed languages that deserve more attention, and by this I do not just mean having a short basic syntax but that their underlying semantics are really well designed. Also they have a great macro system.
2) Nim and especially Zig are incredibly disappointing and trying to use them without gc is a royal road to segfaults. With that said,
Zig is easier to write than C or unsafe Rust in the sense that its compiler has less UB, but that also means the compiler is less free to optimize code.
3) Haskell is great, but apart from maybe being a bit further from mainstream languages, I really wish its standard library could be rewritten from scratch with the benefit of hindsight and new language features, and that texts teaching it took algorithms seriously and made data structures like finger trees more prominent
4) Lua could have been the perfect scripting language in the 90s instead of python/ruby/js/php, if its standard library was more batteries-included and if netscape could have picked it.
And F# took that from OCaml, F# is basically "Microsoft's OCaml" or ".Net OCaml"
Interesting insights. I used to use Elixir quite a lot and really liked it, although it's not the most performant language for compute-heavy work. We used it to replace a Python-based image-processing system in my old job and it not only produced a MUCH more efficient system, but we ended up with far less code to the same job better and with fewer bugs (immutability by default helped here too).
I've used Haskell and Nim for fun, and did pretty much all of the 2020 and 2021 Advent of Code challenges in Haskell, and all the 2022 challenges in Nim (and a few in Crystal too). I'd have to disagree with your second point and say that Nim rarely got in my way or crashed, except when I tried to do some invalid casting of bytes to UTF-8, which did produce an unpleasant segfault. Apart from that it's been great. That said, I wasn't using it without a GC, and relied upon the newish "orc" system which is quite frugal.
I really enjoyed working with Haskell too, although the purity of the type system started to get kinda heavy when I wanted to use dynamic programming and other mutation-heavy coding styles. Using mutable arrays with the ST monad was okay but occasionally confusing and very difficult to debug (you can't just throw in a print statement anywhere), and there doesn't seem to be a mutable hashmap type in the stdlib, so I had to do weird things like manually mapping keys into an integer space and using a vector/array instead.
Nim has memory safety with no garbage collector.
I should have qualified that statement a bit more - all of its mechanisms for memory safety involve some kind of runtime overhead - ie reference counting or garbage collection
Julia synax is inspired more by Ruby than Python.
Thx for nice video :D
ahh thanks for pointing this out, that makes more sense!
And Lua too
I would also add clojure for how short code can look, also clojure has very interisting way oh hadling/embracing null/nil intead of avoiding it.
(->> (s/split (read-line) #",")
(map #(Integer/parseInt %))
(apply +)
(println))
oh nice that one is really concise too! I'm still wrapping my head around the Lisps, might have more coverage of them once I find my footing
0:01 Raku lang!!!
2:03 Nim
3:13 Julia
5:06 Elixir
5:49 Zig
8:00 Lua
9:00 HolyC
They will work for the next 100 years.
julia is the goat of "interpreted" languages. it combines best features of python+numpy (arrays, broadcasting, list comprehensions, duck-typing) R (big default namespace, lots of numeric functions in the base library) and more low-level languages (structs, higher order function & closure inlining, immutable data, isolated scopes that return values). and it will likely get an efficient compiler for standalone executables!
Good subject matter. You used languages I've never heard of. Thanks for the video.
Thanks, part of me was worried folks would say something to the effect of "we've already tried all of these", glad to hear that's not the case!
defo gonna try out crystal, thank you
Crystal does seem like a great language - thanks for watching!
Do Nim's ARC/ORC memory models compete with Rust's memory safety? My understanding is during compile time it will automatically insert memory de-allocations similar to how Rust does it.
I'm definitely not a nim expert - but my understanding was that you can opt-in to reference counting or garbage collection if you want to guarantee memory safety, but if you opt out of that, you incur the same memory management risks that you would have with C/C++. Somebody jump in here if this is wrong!
@@codetothemoon It is not "safe" as Rust, but ARC/ORC are deterministic memory handling as C++ but with some memory handling done automatically for you, like you'll do with shared_ptr in C++
1:10 This honestly really bothers me. The #1 thing I want is (A) There is only exactly 1 way to do things, (B) There are as few languages syntax rules and keywords to memorize.
"loop" violates both. "while True" is definitely superior imo.
I can definitely understand this perspective!
Zig solution in 15 lines. Use anytype for parameter type inference in sum_nums declaration. The one line while loop in sum_nums is idiomatic (as long as it isn't more than 80 chars):
const std = @import("std");
fn sum_nums(it: anytype) !i32 {
var sum: i32 = 0;
while (it.next()) |string| sum += try std.fmt.parseInt(i32, string, 10);
return sum;
}
pub fn main() !void {
var buf: [10]u8 = undefined;
while (try io.getStdIn().reader().readUntilDelimiterOrEof(buf[0..], '
')) |user_input| {
var splits = std.mem.split(u8, user_input, " ");
std.debug.print("{}
", .{try sum_nums(&splits)});
}
}
this "while" line is like the most dense matter in the universe :D
@@adicide9070 oh I thought it was just perl 😀
Can do a review on the V language? Thanks.
actually already did! if you want the original version it's here: ua-cam.com/video/jr1EBaLkjfc/v-deo.html
BUT if you want the version with Primeagen commentary (recommended) you can find that here: ua-cam.com/video/j47Hk5qE9As/v-deo.html
More concise Crystal version:
def get_sum(splits)
splits.map(&.to_i).sum
end
loop do
gets.try do |line|
puts get_sum(line.split)
end
end
You don't even need the get_sum method:
loop do
gets.try do |line|
puts line.split.map(&.to_i).sum
end
end
Overall, nice video.
Zig is next level, will take the crown very soon 👑
interesting, why do you think that is?
@@codetothemoon Great question. So off the top, coming in hot.
- Simple syntax: Zig has a simple syntax comparable to Go, maybe even easier due to it's clever error handling and that it launched with generics.
- No garbabe collector: The issues with Nim as much of the standard library is already relient on the GC.
- Easier to code in the Rust, my god, Rust is amazing but imagine coupling the safety of Rust with a Golang Syntax = Zig.
- Complete compatibity with C libs and C++
- The cross compilation is super fast and is being used by other languages to hit those targets, other languages are leveraging Zig already for compliation work.
- C speed, and faster than C with better compiler. Basically if your a C developer you're being advised to use Zig to compile your C code.
Zig will replace C, that's without a doubt. I even speculate it replacing Golang down the line as the memory management is easy. So why comprimise on speed when you don't need to.
Zig has about two years of catch up planned to reach 1.0 and for editor tooling to evolve.
@@seanknowles9985 does zig have a 100% memory safe subset? That's required by the US NSA guidelines.
@@nicktreleaven4119 good question! I'll throw it to the development team :)
Julia also has Elixir's |> operator, it does the same thing!
On behalf of all Julians I demand that the Elixirites share their trophy with us... or give us a copy of the trophy (we respect that functional programmers dislike shared state)
love the typing sound
Thanks, I actually mic'd up the keyboard for this one thinking folks might like it
@@codetothemoon it adds a little more "depth" to the typing animation, and overall the sound you got is just nice to the ear
Thanks for presenting an example of Julia code as well.
I would love to see Roc in the next one
that's one i hadn't heard of, thanks for putting it on my radar!
If you really want to see this problem solved concisely, you should look at APL. I’m not sure if my implementation is correct (I am a beginner at APL and I don’t have an interpreter available right now) but it should be something like the following:
{⎕←+/⍎v ←⎕ ⋄ ∇}
The braces denote an anonymous function, and ⋄ separates statements. The first statement does IO and all the logic, and the second one is the recursion. It should be possible to do be even more concise (without an anonymous function) but my APL is not good enough to know how to do it.
It's even easier than you think, but kudos for learning. Try this +/⎕
⎕ (quad) by itself means read a line from standard input and evaluate as an APL expression. So if you type in a bunch of integers it'll turn it into an integer array. Then obviously +/ just sums it up and outputs.
there is function composition operator (.) in haskell that works like pipe operator (|>), but in reverse
Actually this one was on my radar, but for some reason I never really thought of it as the reverse of the pipe operator!
the haskell implementation would look nicer by using the `interact :: (String -> String) -> IO ()` function as well as using the point free style: `main = interact (print . sum . map read . words) >> main`. I'm surprised your editor didn't say "Hey, eta reduce!"
Good idea, though remember that interact want (String -> String) so no print in your function. You also don't need to recurse, if you want to do "f" line by line, you can always use `interact (unlines . map f . lines)`. Here, with TypeApplications to specify the Read type elegantly, you can do this :
interact $ unlines . map (show . sum . map (read @Integer) . words) . lines
@@chaddaifouche536 amazing. love the type application! thanks for the correction too, I didn't think interact automatically recursed
In rust I think you need to specify the type for collected because the compiler can't infere it
In lisps the pipe is called the threading macro btw.
I get scared looking at that rust code.
All these languages are just a little collaj of existing language features. Rust is just new. The borrow checker is a completely new feature and it is the best way to statically manage memory.
I agree that the borrow checker takes the cake for the most recent language feature that is truly innovative
There are Odin, C3 & D. Please try them also. Anyhow, I didn't understand a single line from your rust code.
Thanks for pointing these languages out, I've heard Odin mentioned a few times. Would be happy to walk you through the Rust code if you like!
@@codetothemoon Oh, that's great. I would like to use this opportunity to ask some questions.
1. You declared the buffer as mutable. So the compiler can understands that buffer is mutable. But why we need to use &mut with buffer in next line ? Didn't the compiler already understand that?
2. What does unwrap() does ?
@@kcvinu 1. Rust is sometimes quite explicit not because the compiler couldn't understand it but to have it explicitly mentioned in the code for the human reader. Something with references. In C++ you define whether something is passed by reference in the function header and you don't see it in the call at all. So it's more like: This is important information for the reader of the calling code / code that uses a function, so it has to be given explicitly. Rust is not very explicit in other cases (type inference is heavily used)
Edit: That's not a Rust invention, if I remember correctly C# is also already more explicit than C/C++ with these things)
Edit2: You could also immutably pass a mutable String to a function. So the let mut buffer says it can be mutated (in general) and the read_line(&mut buffer) says it can mutated within read_line.
2. There are two things, which are very commonly used in Rust: Option and Result. Option is used when you expect a value, but it could also be that there is no value - the "no-value" is called None. It's basically the replacement for Null. Result is you expect a result (from a function) but it could be that you get an error. It's Rust's error handling (instead of try..catch or the different variants of error return values in C). What both have in common is, that you Rust forces you to deal with the "extra" cases (None, Error). The "dirty" way is using unwrap(), which says: I know what I'm doing, there is a value inside this Option/Result, just unwrap it and give it to me. If you're wrong and there is a None or an Error the program will crash.
Interesting video... but I like the end "Has my love for rust waned? It hasn't"
Very good point. Other languages can have maybe better syntax changes or some ergonomics but that's not worth losing memory safety with no garbage collection. If some other language tried to implement memory safety with no garbage collection with better ergonomics... Rust would probably just adopt those ergonomics too because it's constantly improving and gaining ergonomics. It's hard to even fathom something better unless there's some massive rift/scandal in the main contributors and people who manage and use the language which would be surprising because the community is so good.
That was very concise, thanks.
you're welcome, thanks for watching - brevity is one of my core tenets 🏎️
|> is just function application (call), same as $ in Haskell just in opposite direction. Nothing to do with map.
Please talk more about crystal, I really love the language but it doesn't have much traction😢
I was really impressed by it too and was also surprised by its lack of popularity. i hope to be able to cover it more!
I used haskell in a universaty course about programming languages paradigms and general stuff. I haven't used in since, but I really do remember how beautiful and concise the code was
yeah it sure is - i had to use ocaml for a course in university, and I remember being really frustrated with it. I was too shortsighted to appreciate the beautify of it back then, but I sure do now
nim callback can be made simpler with "sugar" module
thanks, didn't know about this!
In Haskell instead of using map with a lambda you can use list comprehension. It's significantly more readable imo
sum_fn ws = sum $ map (\s -> read s :: Integer) ws
sum_fn ws = sum [read s :: Integer | s > main
After seeing Haskell's `interact` mentioned in the comments, I figured out how to make the Idris version even sleeker:
import System.REPL
import Data.String
main : IO ()
main = repl "
" (cast . sum . map cast . words)
Julia synax is inspired by Ruby, not Python.
Thx for nice video :D
ahh thanks for pointing this out, that makes more sense!
That Rust implementation made me screech autistically a bit. split_ws -> parse -> sum, no collect.
Wow yes, thanks for pointing this out! Sadly I don't think it's the only version of this program that I implemented sub optimally...
Superb video!!
thank you!
Recursion is GREAT if you know what you're doing, it just requires different syntax and can cause memory uncrease (although I like minimizing vars in functions anyway \_[-_-]_/).
would love to see Gleam in the next edition of this video!
yeah i've been itching to give Gleam a spin, it looks really nice!
I don't care about how many people tell me Nim is good... I hate Nim! Also, you shouldn't be able to change how the core language works. That sounds like a nightmare.
I wanted to use Crystal for a long time but Windows support was no good at the time. I ended up downloading and installing V, it was easy and quickly to get going, I learnt most of the basic syntax in less than a week. Top G Video!
Isn't V nice? It's so simple, but it's so fully featured (except for async). I love the UI and CLI modules.
V has a long history of overpromising and underdelivering. Like promising Rust-like automatic memory management without lifetime annotations and GC and ending up with just leaking memory and using a conservative GC. V is for vapourware.
For the elixir version, you can simply do this
IO.read(:line)
|> String.split(" ")
|> Enum.map(&String.to_integer/1)
|> Enum.sum()
|> IO.puts()
nice, that is much more concise. Many (most?) of the solutions turned out more verbose than necessary. I really enjoy seeing the more optimal versions from those that are more proficient in each respective language
Nim certainly is trying hard to have the memory safety of Rust.
5:35 If you like that feature, you might love FSharp, who come up with this operator and is very cute overall
Like Elixir, just statically typed with the most accurate type inference you had ever seen
And dotnet as a platform
stop spreading this misinformation, the pipe operator has been around long before f#
I know all those languages. Should I consider myself a language nerd ? Another great language inspired by Ocaml (just like Rust) is Haxe.
it sounds like you do qualify as a language nerd, and I'd see that as a good thing! 😎 re: Haxe - I just checked my "list of tech to look into" and it was already on there! Would you recommend learning Ocaml first or going straight to Haxe?
@@codetothemoon ocaml is an awesome language, but I think that Haxe is a lot easier to pick, and has the impressive advantage of being designed to compile to other languages (a lot of them). Also, it has a lot of features that will remind you the best of rust (like static extensions )
@@codetothemoon if you want some example usages beyond games, I have two repositories. One for creating neovim plugins (using Haxe instead of Lua) and another one for creatingg hammerspoon spoons (again, Haxe compiling to Lua)
Which font are you using? Amazing video, keep up the good content!
I think it's monolisa, could be wrong
Am I missing something or did you miss the split delimiter in Nim example and passed an empty delimiter instead of a space for Julia example?
2:16 Nim - ooh, it appears to fix my biggest complaint with Python when programs get larger, that there is no compiler checking of function parameters and interfaces, and that the hints are just ignored! Forget the extra external tooling/linting pass.
3:37 Julia - nice, visible control structure with symmetric open/close. Something about Python and YAML feels so asymmetric, with the block just dangling in the air and no clear closure, unlike Java/JSON/XML.
6:04 Nim - sigh, yet another language that evidently hates vector and matrix math. If you don't support basic operators on vectors and matrices, then as a language you're getting dumped into my garbage bin, because I do way to much graphics programming to be calling v.multiply(w).add(t) (reminiscent of Java) instead of just v * w + t.
6:32 Nim - though, I do find the use of 'const' for constants and 'var' for variables just lovely, compared to 'let' for constants and 'let mut' for variables (ugh).
About your 3 comment, Nim does support operator overloading and custom operator (including utf8).
You just have to declare a function like
proc `+`(mat1, mat2: matrix): matrix=
//Code
And yeah, having to write let for each variable is a bit annoying by time but Nim have let blocks for declaring a bunch of variables at onces, example:
let
X = 5 // Nim will infer the type of the expression
Y: int // you don't need to set a default value
X: seq[int]// you can do any type in a let block
You also have var and const blocks
You can define any operator you want in nim. Could even have one for cross product and so on
How to get the |> operator in Haskell:
infixl 1 |>
x |> f = f x
Alternatively, it is already in the base library, you just have to import it. It's called & but you can alias it to |> if you want
import Data.Function ((&))
(|>) = (&)
You have such a very good Doom Emacs config can we get the link to it please ?
I'm not doing anything special, I just uncommented the relevant programming languages in init.el and switched to the monokai-pro theme. The only real config I had to add was some stuff to get Zig working, since I don't think DOOM supports it out of the box.
How about Clojure?
There is 1 language.. i dont personally use it but i want to... AS3 (ActionScript 3)... it was used alot but is less used because of flash dieing (ruffle flash is bringing it back)... to be honest... AS3 is over looked alotl
FWIW I believe Haskells & operator in Data.Function is the same as elixirs |> … doesn’t look as cool though.
ohh I didn't know about this, I'll give it a spin - but yes I agree |> is much more aesthetically pleasing 🙃
What about Ada?
haven't tried it!
Hey, would have been cool to actually show comparison between 'business' LOC and ones that glue it in-between. I never actually expected zig to be so cumbersome. Anyways here is the same thing in C#, I think it's a mix of both worlds in terms of expressiveness and fast-ness Console.WriteLine(Console.ReadLine().Split(' ').Sum(int.Parse));
Zig is a simple language as c is. So it can be more verbose but the learning is easier and the code is more predictable.
c# is just wrong, pointless overuse of capital letters and braces.
@@terryriley6410 cs is the other name that it has, but still has 2 characters :(
I think Julia has the same pipe operator as Elixir
thanks for pointing this out!
Julia has the |> operator too…
Zig is so much fun to use.
what makes it fun? I got a bit of exposure to it while making this video, but I still don't feel like I have the whole picture...
I really thought that dark spot on your shirt was a stain until it moved when you moved.
Will be there video about your DoomEmacs?
Would you be interested in that? I'd love to make one, I've been unsure about whether folks find editor related videos valuable - especially because I believe most folks watching are using VSCode. Also I'm still on the fence on whether to move forward with Doom or Neovim...
@@codetothemoon I'm personally a NeoVim user but I'm would like to see what deal on the "other side". So I'm would very like to watch video about that