Tuesday, January 29, 2013

Thoughts on Some Languages

    For my first real post, I'm going to describe what I like/dislike about several different languages I've used (at least a little). I feel that it's a good way to introduce myself, in a way, to show all you readers what I think about different languages/paradigms. At the end, as as special treat, I'm going to give you some example languages that I'm thinking of creating! So, onto the languages:

C#
    C# is a modern .NET class-based language. It has nice features such as reified generics, operator overloading, automatic conversions, lambdas, and a limited form of type inference. C# is my favorite language because it's what I've used the longest; it feels "clean" to me, much more so than any other language I've used, and I love it because its features work very well together.
    The reified generics mean, basically, that there is no type erasure, and that you can figure out what the actual type argument is at runtime. I'll go more into this later, but Java's type erasure is very annoying at times, because you cannot even make a generic array!
    C#'s operator overloading is a very important feature when one is using user-made structures that can have mathematical operations performed on them. For example, would you rather see vector1.add(vector2.mul(vector3)) or vector1 + vector2 * vector3? I, personally would rather see the second version, with the basic math operations we all know and love.
    Oh, but, but, you can make the + operator do anything you want to! And that's dangerous! Don't give users so much power! Response: what's the difference between making the + operator do something wierd, and making an add(int, int) function do something wierd? It's just naming! Anyway, there are also conversions in C#, which can make some code clearer/cleaner, because you do not need written-out casts for very similar objects. 
    Lambdas are one of the best features; they are useful for so many things, including, but not limited to, callbacks and user-supplied functionality (no need for single-method interfaces!). I'm too lazy to think of more examples right now...
    C# has a limited form of local type inference; you can say var dict = new Dictionary<string, int>(); instead of Dictionary<string, int> dict = new Dictionary<string, int>();
    And, last but not least, C# also has Visual Studio! Some of you may be saying, "but I don't like Visual Studio!" But I do, and since this is my blog (thus my opinion), I'm going to shamelessly say that VS is a bonus point for C#. Now, enough about C#, on to Java!

Java
    Java is, unfortunately, my least favorite language. However, I've got an explanation, so don't start the flames yet, Java-lovers! First, I don't particularly like the Java standard libraries. I haven't used them as much as the .NET libraries, but to me, at least, .NET feels more orderly. As an example of the difference, using .NET+C# you can write string[] lines = System.IO.File.ReadAllLines("C:\Users\...\...\text-file.txt");, whereas in Java, I'm still not sure how to do it, and I've already browsed the internet and StackOverflow to try to get the answer. Perhaps .NET simply has more convenience methods? I don't know.
    Another thing I don't like about Java is the lack of operator overloading (see C# above) and lambdas (again, see C#). Java also uses type erasure, so you cannot even make a generic "transform array" function! In C#, I would write this function as:
  U[] transform<T, U>(T[] array, Func<T, U> convert)
  {
      U[] result = new U[array.Length];
      for (int i = 0; i < array.Length; i++)
          result[i] = convert(array[i]);
      return result;
  }
In Java, you would get an error on the first line of that function; something along the lines of "cannot create generic array".
   Last, but not least, the Java mindset is to create getters and setters for everything, so that you "future-proof" your classes. The idea is that getters and setters allow you to later change the code of these functions, without modifying client code. By simply exposing a field, when (if ever) you want to add verification/other changes to the field, you must at that time add the methods, thus breaking client code. C#, however, follows the Uniform Access Principle, at least in regards to properties and fields.

F#
    F# is undoubtedly my favorite functional language. First, it is built on the .NET framework, which means libraries and idioms that I'm used to. Second, it has full Hindley-Milner type inference, which means that you, in some cases, don't have to provide even a single type declaration; the compiler will figure it out for you. F# also allows arbitrary operators to be defined, with prefix-based operator precedence, so it allows you to stay on the well-beaten path of operator overloading, but you are not stuck with the default operators that the language designer built in.
    F# is an impure functional language, which, I think, is the best of both worlds: functional and imperative. Granted, F# does sometimes make it harder to use imperative styles, but that's because it's a functional language!
    One thing I really loved about F# was the workflow idea. F# workflows are basically Haskell monads, but the description of workflows in the book Expert F# explained the idea much better than anything on the internet taught me about monads.
    One last thing (that I will write about) that I love about F# is the pattern matching. It allows for very concise value destructuring and is very clear to the code reader about what is going on.

Ruby
    Ruby is an extremely dynamic, completely object-oriented language with a beautiful syntax. It follows the idea of "everything is an object" as much as it can; all values are objects, including primitive values such as integers and floating-point numbers. The lack of static safety (including type-safety) brings ease of use, but also causes me to fear silly mistakes, like typing array.lengt instead of array.length. In most languages, an error such as this would be caught at compile time, but in Ruby, there is no compile time. So do most people get their fears assauged through unit tests? I've never used one, believe it or not (but I've also not used Ruby very much at all...)

Last, But Not Least: Tcl
    TCL stands for Tool Command Language. It is a word-based language; every command is of the form word arg1 arg2 arg3 ... Every word is a string, and every command can interpret their string arguments in different ways. In one command, a string might be interpreted as a number; in another, a list. I think Tcl is a cool language, but the idea of "everything is a string" is, I think, wrong (to put in plainly). Not everything is a string; for example, C#'s Object.

(Almost) The End of the Journey
    ... in regards to languages I've used/learned. I like something about all of these languages (except Java -- I'm sorry, Javalovers!), and I hope I've explained why I like certain features. Now I would like to show you some languages that I've thought about.

A Functional Language
    This functional language is kinda-sorta minimal. I suppose it would (initially) have a type system that supports only variants (sum types), tuples (product types) and functions. It would have a fully generic type system, and some form of type inference. It would also have type classes and type class instances, Haskell-style. I think an example is in order:
  type Int = Zero | Succ Int
  let add Zero b = b
  let add Succ(a) b = Succ(add a b)
  class Addable T =
    (+) : T -> T -> T
    dec : T -> T
    zero? : T -> Bool # Bool defined as: type Bool = True | False
  instance Addable Int =
    (+) a b = add a b
    dec Succ(n) = n
    dec Zero = Zero
    zero? Zero = True
    zero? _    = False
  decl mul : 'a -> 'a -> 'a where Addable 'a
  let mul a b when zero? a = b
  let mul a b = b + (mul (dec a) b)
This example shows a definition of the Peano integers, an addition function, an Addable class, a class instance, and a function that requires a type classification.

A Word-Based Language
    This word-based language would be like Tcl, except that not everything is a string; in other words, it would be like typed Tcl. An example:
  proc fact n {if [= $n 0] 1 {* $n [fact [- $n 1]]}}
  set n [to-int [ask 'Enter an integer: ']]
  puts [fact $n]
The variable n would be typed as an integer, not as a string. Unlike Tcl, curly brackets { } would signify a code block, instead of an unparsed string. Or something like that. As an aside, I think that a word-based language would be a good OS shell language, because such languages often make it easy to use plaintext in the code (as in [ask Integer:]), and have a simple, command-based syntax. In this model, if a command could not be found, then an executable with the command's name would be searched up and then executed with the arguments.

Actually The End
    Wow, that was a long post. Probably the longest one you'll ever see from me. Unless I feel like writing more... eh. Maybe. I've gotta work on my calc homework now!

I Lied In The Previous Heading
    ... because I have one more thing to say, before I go: next time is monads! I'll try to describe how I think about them, and hopefully have a better (or at least different) description than the many guides there already are.

1 comment:

  1. Hello Grant.
    I am a Java lover who has been working with the language scarily since before you were born, not that that's really relevant...you've made an impressive blog post and I have no doubt I will be working with, if not for, a young individual of your talent someday.

    That being said, I would offer a piece of friendly advice. Consider your users a bit more. Java (server-side) is beloved more by those who have to maintain it and those who have to use it than those who have to write it. In contrast, Ruby and Python are beloved by the authors, but hated by those who inherit their apps. Ruby has some of the worst scalability issues I have ever seen. You can write the world's most awesome code in Ruby, but if it falls down under load, you'll be in severe trouble. Python is another example. We paid a guy to write testing code and he chose Python. It was a beauty to look at and ran well on his macbook pro. Getting it to run in Ubuntu was a 2 day task. Getting it to run on Red Hat Enterprise Linux afterwards was a half day task. Getting it to run on Windows was another day and a half. Needless to say, we discarding his program and all of it's beautiful code after it started breaking on kernel updates.

    Those who pay my salary are always interested in the results, not how I got there. That's why I love Java. It works well across most servers, performs better than .NET and even most native code (unless you're really talented with C/C++). The standard Java libraries are dumb as dirt. However, most of us rely on open source libraries to fill the gap and the end result is software that performs well, works anywhere with little modification, and improves in performance with age as new JVMs are released...as well as is usually insulated from issues due to OS/kernel updates.

    Finally, consider that if you don't like Java, there are many languages that compile to bytecode that are more pleasant to work with like Groovy or Scala. I greatly prefer Groovy over Ruby, but that is because I know it performs well and is cross-platform.

    You're off to a great start and have a nice blog working. I wish you the best and hope that I can hire a bright young talent like yourself once you make it out of college.

    ReplyDelete