📜 ⬆️ ⬇️

As I rewrote the project with JavaScript on Scala


I can never walk! Because I'm crawling.
—Quotes of the Great

I was always taught first of all to say hello, so - hello. Today I will talk about the creative (and not so) pain, suffering and pain that I experienced during a certain period of my life, which I will designate as a PROJECT. At first it was on JavaScript (node.js), and now it is on Scala (Play). I must say at once that I am one of the most subjective villains in the observable universe, therefore some turns, statements and others like them can be perceived by respected readers quite ambiguously. In short, I warned. And I have another small request - if you have already taken to read the article, then do not rush immediately to scribble exposing comments. Read on. I'm not Pasternak, telling the truth. And in general, almost all points of contention somehow illuminate, explain.

Prologue


But for starters, I allow myself a small digression and tell you what I did and how long it started.
About a year and a half ago I was just before the choice of a topic for a thesis in my technical university. Of course, I could otmazatsya some banal site for a car wash, another new design concept for the Kofekhaus site or something similar (by the way, these are real diploma projects, and it makes me crap, but omit it). But we are stubborn, and yes even professionals in their work! Easy ways are not looking for and blah blah blah.
It should be said that by that time I had about 3 years of working experience behind me and ~ 6-7 years of programming programming, specifically, on the web. Therefore, there were no questions about the implementation before me. There is a topic left, that is the same PROJECT. Local guys need to know a funny article about developing through suffering .
So at that time I really experienced suffering when working together or studying the next PL with my comrades. I needed a tool like pastebin, i.e. banal as a square (sent - copy-link link - shared), but still with the Google Docs counter, namely, simultaneous editing of the code. Agree - it's cool when you just see how someone put the cursor on the cant and fixed it in two keystrokes. Well, any faster than the same code duplicate every time, change links. Pain.
And so I rummaged through these your Internet sites in search of such a service, and ... did not find it. Of course, I know about plugins to the same Eclipse, Sublime, etc., and even know about the whole standalone-solutions. But I did not find something simple like pastebin. From here and started my countdown PROJECT.

Chapter 1. JavaScript


From the prologue, you could have already formed a short TZ, what the necessary service is and how it should be performed.
We have, roughly speaking, some user chat, where instead of a chat there is a code over which everyone pills. How? In short - I do not like flash, I want web sockets.
At that time I was sitting tight on PHP, but on it write a WebSocket service, where full-duplex connections can hang for about infinity - the direct road to Hell. Therefore, I turned my attention to node.js as a WS-service, and generate statics and give them a puff. And you know what? It was cool. I put the prototype in just a couple of days. Everything worked , and it was an indescribable feeling. As if you just realized string theory. Or he guessed where the information in a black hole disappears. Well, you understand me, yes? And then just released nginx'a release which was able in proxying WebSocket.
Lyapota
// ,        .    . var sockjs_im = sockjs.createServer(sockjs_opts); sockjs_im.on('connection', function(c) { //     sender.setValidTimeout(c); //   c.on('data', function(message) { sender.process(message,c); }); c.on('close',function() { connection.removeConnection(c); }) }); var server = http.createServer(); server.addListener('upgrade', function(req,res){ res.end(); }); sockjs_im.installHandlers(server, { prefix: config.get("path") }); exports.startWSServer = function() { server.listen(2410, '0.0.0.0',function() { console.log(' [*] Listening WS on 0.0.0.0:2410' ); }); } 


But back to business. I grabbed this hammer and started banging everything around, and then let him figure it out himself - who is the nail, and who is the camel. I took the most hipster technologies: SockJS as a client-server for WS-connections, MongoDB as, you won’t believe, a database, Ace Editor as an editor on the client, made them blind and started writing binding and logic.
Here I’ll make a little note - the project was a graduation project, so I had to do everything quickly and cheaply and immediately, and I also had to work, hand over something.

A month later, after another energy bank, I, tearing my red eyes off the screen, realized that a monster was born, a creature, something that would be better to breathe immediately. No, it worked, without failures, the functionality was almost all ready. But the way it worked - inspired sacred horror. I made a fatal mistake and did not rewrite the prototype. I increased it. The code has become complicated and redundant.
From this point on, work on the project turned into torture. Adding a new feature or chip required a lot of effort. I felt like Sisyphus, only the stone was also square. Suffering.
The logical question is - what are you up there? Here is a small list:

And here comes the Minotaur that guards the belts of the seventh circle and says:
- Hey, man. Well, you're a fool. Wrote devilry, break his leg, he himself confessed. What is your problem?
And you know what I will answer? This is all javascript. That is, do not get it wrong, I do not mean that I am so cute and fluffy with iq >>> infinity. I mean, the language itself pushes you to write this way and not otherwise. A kind of serpent who whispers:
- Come on, mate, stick-sss here is a quick third callback in arguments, nothing will be lost-ccc ....
And such a long red tongue before your eyes.
It looked like this
 /** *  .   -     ... */ var loadSession = function(sessid, callback) { if(typeof sessid != 'string') return; var sess = sessions.getSession(sessid); sess.setLoadCallback(callback); } ... /** *  ,  ,   ,     . *   -,     . */ loadSession(msgObj["sessid"], function(sess) { var r = rooms.getRoom(msgObj["room"]); r.setLoadCallback(function() { r.addUser(connection, sess, function(newUser) { if(newUser!==false) { var outMsg = { action: "newUser", data: newUser } r.broadcast(connection, outMsg); r.write(connection,{ action: "join" }); }else{ error(connection,{ error:true, errsmg: "something went wrong" }) } }); }); }); ... 


Perhaps this is all energy, and no minotaur with a serpent came to me (crawled), but it turned out what happened. The diploma was handed over, but the creature lived to live, if it could be called life.
After several attempts to refactor this monster, or even rewrite everything into a coffeescript, I abandoned all this devilry for better times and left to ride around Europe. Yeah, I fell so deep and ran so far from the nightmares of the past!
')

Chapter 2. Scala


Half a year has passed. Monster all existed, but I had no desire to finish it and release it in production. Opening the JavaScript code, I had to urgently run after a metal basin, which was used for ringing vomit.
And then, lost on the Internet, I found myself on the Play Framework website. I do not remember what attracted me and delayed on the site, and is it important? As a result, one day later I was digging around with the framework, wrote the first application and enrolled in a Scala course at coursera.org.
I can not say that it was easy, especially at the beginning. Of course, python or puffs are simpler, but with Qt / C ++ and Java backgrounds, I figured out the pins pretty quickly, at least in the main points. In order to enjoy the implicit conversions and parameters, co / counter / invariance and others like it took you to add your Google skill in search of various examples and documentation in order to get a general picture of what is happening somewhere under the hood. And yet for some time I felt like a stupid felt boot, although there is an opinion that this is normal.
And so, having a little stuffed my hand, I decided to see how Play is able to use websockets. And here I was poured with icy water like a tub. The first reaction was simple - WTF ??? Where are the nice and affordable solutions, what is this hardhaircore with near-low-level Iteratee / Enumeratee? Yes, nothing to do with that JavaScript blunder. Give me back my push and onMessage !
Fuck you, not channels
 def index = WebSocket.using[String] { request => // Just consume and ignore the input val in = Iteratee.consume[String]() // Send a single 'Hello!' message and close val out = Enumerator("Hello!").andThen(Enumerator.eof) (in, out) } 


However, being taught by bitter experience of too simple solutions, I decided not to give up and again picked up ... No, not a hammer. Google As a result, there was a pleasant approach through
Concurrent.unicast
 val promiseIn = Promise[Iteratee[String, Unit]]() val out = Concurrent.unicast[String]( onStart = onStart(promiseIn, r, userSession), onError = onError ) (Iteratee flatten (promiseIn.future), out) ... private def onStart(promiseIn: Promise[Iteratee[String, Unit]], ...): (Channel[String] => Unit) = { ... (ch: Channel[String]) => { val channel = new ChannelContainer(ch) for (optUserConnection <- isConnectedF(r, channel)) yield { optUserConnection match { case Some(userConnection) => { val in = Iteratee.foreach[String] { MessageController onMessage (r, userConnection, channel) //bind handler for room and this connection } map { MessageController onDisconnect (r, userConnection, channel) //handler for disconnect } promiseIn success in //success promise and fill it with iteratee } case None => channel eofAndEnd //in case of some troubles with new user creation - close connection } } } } 


Yes, I agree. It’s still looking not so cute and very cumbersome, but it allows you to use canonical channels created for exchanging messages, stuff these channels into wrappers, transfer them to actors in messages ... Actors!
Actors in Scala. Turkish delight of my heart. BEST PARADIGM EVAH! Well, or exactly what is needed for my purposes. Room - actor, room manager - actor. And even customers are actors. It is logical to join the ideology of messaging between users. By the way, the developers of Play have also read this topic, and since the recently released version 2.3
WebSocket connections are also actors
 import play.api.mvc._ import play.api.Play.current def socket = WebSocket.acceptWithActor[String, String] { request => out => MyWebSocketActor.props(out) } 

 import akka.actor._ object MyWebSocketActor { def props(out: ActorRef) = Props(new MyWebSocketActor(out)) } class MyWebSocketActor(out: ActorRef) extends Actor { def receive = { case msg: String => out ! ("I received your message: " + msg) } 


And it is beautiful when the Universe hears you. My signals were reflected from a surface in light years from here, on the planet of butterflies popping rainbows, and flew back. The quanta got confused, Alice and Bob found each other. And I found peace.
And why? Because Scala in most cases just looks at you like shit, if you do something not by the rules. She seems to be telling you:
-Buy, I give you native support for Future in the form of monads, JSON messages to the instances of case classes you need, give you the opportunity to inherit a set of traits, I check you, watch every sneeze when compiling, do not play games with me. Do fine or return to your Hell in the Minotaurs.
Strict but fair companion. Bearded Heisenberg programming. Yea, Scala, biaaatch! And Martin Oderski himself bumps you in the back.

Epilogue


So, what can be said after such a confused flow of water of consciousness, what is the conclusion?
The first . I do not compare languages. Save pasta. Comparing Scala and JavaScript is like comparing lion and grandmother's pies. Yes, you can eat grandmothers and you will be full. But the lion - is unlikely. But the lion can eat you. These are different languages ​​for different tasks. Who will write frontend on Scala ?
The second . I compare the possibilities, or rather, the approaches that provide languages ​​and platforms used in solving specific problems. Gentlemen, but callbacks are not packaged! This is a fucking verdict.
I can take and make in one line of a dozen Future one single and wait for its execution. Just because Future is a monad, everything looks simple and natural, in the spirit of language. For callbacks, I will have to write a wrapper that will either count the number of completed functions, or become a full-fledged Deferred / Promise. Yes, yes, I know about the existence of such libraries for JavaScript. But these are just bloated wrappers to those callbacks. Closed hell circle.
Third . Would I write JavaScript better now then? Sure. It might even have looked and worked simpler. Experience in solving problems of the same type is an experience, you won’t drink it. Would it be better than on Scala? Is not a fact. Yes, on Scala in places you have to write much more code, but I damn sure about it! I don’t need to write hundreds of tests for one method that accepts an object from JSON to make sure that everything goes as I had intended. That some kind of script kiddie did not substitute a string for an array, an array for a number, etc. For me, it will make language, platform, compiler.
Fourth . Scala is hard , Scala is hard , Scala is hard . Thousands of them. Blah blah blah. Do you know what is difficult? Keep in mind the context of this and hundreds of aspects that are constantly trying to nerve nodes Tru JavaScript Ninja. Seriously, to write a big project with a bunch of logic on JSe, you need to be much more expert than on Scala. I felt it on my ass. Respect those who every day goes to this unequal battle. You are cool, no kidding. JavaScript is not simple.
But for myself, I decided not to suffer. What for?

Source: https://habr.com/ru/post/227731/


All Articles