6 ms·
I'm not a user of OCaml, but here is a counter-argument for C#: First-class functions C# has closures, and can pass function pointers with delegates. -- imm
by hacker_9 11y ago
I'm not a user of OCaml, but here is a counter-argument for C#:
First-class functions
C# has closures, and can pass function pointers with delegates.
--
immutability is the default
Mutability can be a useful tool in some situations, but it is often unnecessary and potentially harmful.
I think this point has probably been argued to death by now, but I still can't see this as a good idea. 'Side effects' are really just a reality of software development. For example if I want to fade in a GUI element on a page, I don't want to create a new element for every transition change. Theres nothing wrong with mutable state, but it is nice to have immutability for some use cases. In this case c# has valuetypes, and the const/readonly modifiers.
--
Strong static type checking
Yep C# has had this since the beginning. And you can even use 'dynamic' if you really want which moves all type checking to runtime.
--
Algebraic data types and pattern-matching
I feel like these are more buzz words than actual substance. The Html example given also doesn't seem exactly clear to me how the operations are being performed. Here's a pseudo c# way of doing it:
rootHtmlElement
.SelfAndDescendants()
.OfType<DivElement>()
.Where(d => d.Attrs["class"] == "foo" || d.Attrs["class"] == "bar")
.Select(d => d.ChildElements.OfType<TextElement>().FirstOfDefault())
the typical object-oriented implementation of this in Java would involve multiple subclasses of a common base class.
I assume they mean that all element types would inherit from a base IHtmlElement? This seems like a good thing to me.
--
Type inference
Again C# has these abilities with 'var a = 0' for example. It won't infer method return types, not because it couldn't, but because it's just generally clearer to have the explicit return type written there in the code for the reader.
--
a replay-capable debugger
C# has intelliTrace for this. Though I expect both suffer from the same problems, slow execution while in use, and huge memory footprint.
- cwyers 11y agoC# has actually been trying to incorporate as much from ML languages and Haskell as it can (look at Linq, for instance). And of course, F# is an ML family language with a strong resemblance to OCaml that runs on the CLR. Trying to attack OCaml by noting that C# has a lot of the same features kinda misses the arrow of causation here.
- hacker_9 11y agoDid you even read the article? OP says in the second paragraph OCaml includes many features that are not available in the more mainstream programming languages listed above, which included C#. My post was to show that C# does in fact have those features, not to argue the origins of said features.
- nv-vn 11y agoThat's true though. You argued why they're not necessary but not that they're there. And C# still lacks a solution to the ML module system, GADTs, and other advanced features of OCaml.
- mbrock 11y ago> I feel like these are more buzz words than actual substance. Um, both those concepts (algebraic data types and pattern matching) are very clearly defined, classic language features. Why would they be substanceless buzz words?
- hacker_9 11y agoWhen I think of 'pattern matching' I think of some complex idea like brain-level parsing of text to an AST via matching of syntax patterns. But what OCaml pattern-matching really is is just the equivalent of a C# switch statement, or if/else-if statements.
- mbrock 11y agoYou might think of that but that's not what it means in this context. ML-style pattern matching is a concise way to match nested structures and simultaneously bind variables to arbitrary subelements, which is particularly useful when combined with algebraic data types. Both are classic features of ML and derived languages since the early 70s. They're not buzzwords just because you don't know what they mean.
- thinkpad20 11y agoYou're right that pattern-matching in functional languages resembles a switch statement. In a way it generalizes the concept of a switch statement, in the sense of "doing different things based on the shape of the object at runtime." However, it's quite a bit more powerful. Here's a simple and oft-used example, which is option types. They correspond roughly to nulls so let's compare two blocks of pseudocode: // traditional if myValue == null: print("nothing here!") else: print("value is " ++ myValue) // with pattern-matching switch myValue: case None: print("nothing here!") case Some(thing): print("value is " ++ thing) The two appear similar on the surface, but in the first example, we are relying on the programmer to check that myValue is not null. If he or she fails to check this, we'll likely throw an exception or worse. On the other hand in the second case, we do not need to trust anyone; instead we guarantee that there will be a non-null value added to the string, because it won't be possible to "extract" the inner value from myValue without entering a context in which myValue has a `Some` form. There is a lot more safety this way, since it's guaranteed by the language rather than the programmer. And not only is it more safe, but it's a lot clearer and more expressive as well. Consider a type with more than two variants, or holding multiple internal values rather than just one. The imperative if/then/else solution rapidly becomes verbose, obscure and error-prone, while the pattern matching case is just as straightforward as the above example. It might seem weird on the surface but after a few times using it it's the simplest thing in the world and you'll wish you had it in other languages. In fact, Rust, which isn't (primarily anyway) a functional programming language, makes heavy use of pattern matching.
- sklogic 11y ago> var a = 0 This is not a type inference. It is a mere type propagation.
- hacker_9 11y agoWell, no, the term 'type inference' is used to describe var. See here for more: http://stackoverflow.com/questions/479883/how-good-is-the-c-sharp-type-inference http://stackoverflow.com/questions/479883/how-good-is-the-c-...
- lpw25 11y agoIt is, but it is worth noting that when OCaml or Haskell programmers talk about type inference they are usually referring to global type inference which allows an argument's type to be inferred from its uses. For example: let f x = x + x will be inferred as int -> int.
- sklogic 11y agoThey can call it any way they fancy, something like "Majestic Royal Type Procession" would have been equally ambitious. But in a context of ML, type inference is a Hindley-Milner inference or an equally powerful equation solving system, and not just a pathetic "assign the RHS type to LHS blindly" as it is in C++ and C#.
- todd8 11y agoIt's easy to underestimate what the advocates of functional languages are talking about. One can pass a function (pointer) to another function in C for example. Lots of languages have library support for pattern matching via regular expressions. Types are supported in various ways in most programming languages. However, these things aren't even close to what is being talked about here, and that is why the comments are being down voted. If you are interesting in studying and comparing programming languages, I suggest picking up one of the five big functional languages: F#, OCaml, Haskell, Scala, or Clojure. Scala and Clojure are great, they run on the JVM, they perform well, are practical and pragmatic, but are probably not the best way to learn "functional" programming because they include too many additional features or leave out some features that some consider important. I would recommend starting with Haskell and then comparing it to the others to see if one of these could be helpful in your own programming. There are good, free resources for learning functional programming, see [1]. [1] http://learnyouahaskell.com http://learnyouahaskell.com
- mercurial 11y ago> I think this point has probably been argued to death by now, but I still can't see this as a good idea. 'Side effects' are really just a reality of software development. For example if I want to fade in a GUI element on a page, I don't want to create a new element for every transition change. Theres nothing wrong with mutable state, but it is nice to have immutability for some use cases. In this case c# has valuetypes, and the const/readonly modifiers. Mutability is one side effect. OCaml is not Haskell and doesn't have a type system baked in. Sure, sometimes you need to cheat, but mutability as the general rule is crazyland. Immutability gives you a compile-time assurance that your data structures are consistent and valid, as long as the tests in your constructor function are correct. > Yep C# has had this since the beginning. And you can even use 'dynamic' if you really want which moves all type checking to runtime. Well, it has strong static type checking... within the bounds of what its type system supports. Which is fairly limited. > I feel like these are more buzz words than actual substance. Others have responded to that, but in short, your impression is wrong. > Again C# has these abilities with 'var a = 0' for example. It won't infer method return types, not because it couldn't, but because it's just generally clearer to have the explicit return type written there in the code for the reader. C#'s "type inference" is cute, but you can't seriously compare it with OCaml's.
- victorNicollet 11y agoC# developer here, with a long history of OCaml development (and I'm still using OCaml knowledge as a filter for recruiting C# developers). First-class functions in C# are nice, nearly as good as those in OCaml. The main annoyance is the incompatibility between Action<> and Func<> (and, in general, the fact that C# uses void rather than unit). -- Immutability: for every mutable class in our code base, there are three or four immutable classes, and `readonly` is likely the most frequent keyword. Among these, we have an entire hierarchy of dozens of classes that are entirely immutable (and make heavy use of immutable containers from Microsoft) because it's still the easiest way to implement a multi-reader, single-writer thread-safe data structure. You can survive without immutable-by-default, you can survive without OCaml's { record with a = b ; c = d } syntax, but it gets very, very annoying to work without {< self with a = b ; c = d >} in an object-oriented language. Algebraic data-types: I'm mostly writing compilers (yes, in C#, don't ask), and instead of writing a simple | SetLineOp of param * param * index I have to define a new class, three readonly fields, a constructor to set them, equality operators (R# helps, but still...), and five or six lines of visitor pattern boilerplate in order to get the "you missed a case" warning in pattern matching. This gets very annoying very quick, and there is no practical benefit to having all those classes here. -- Type inference: C# does manage to be fairly clever, although not as clever as OCaml. I have seldom found it to be lacking in general use, aside from the ability to define local functions (var f = a => { ... } does not work, you have to spell out the type of f). There have been a handful of times when I was trying to do something very clever and found myself limited by the type system, and had to write annotations myself. And one time, I had to reimplement GADTs with generics and reflection. It was painful, but it works. However, with a few minor tweaks (e.g. an option type), I would rather have the C# type inference than the OCaml one. The reason is that, if I want to do something very clever, I will not find myself limited to code that I can actually prove to the OCaml compiler as correct: I have, time and time again, resorted to reflection and code generation to work around such situations. In other words C#'s Obj.magic is a lot more powerful (and safe, and expressive) than OCaml's. A fairly good example is Eliom's way of expressing the parameters of a service. In C# you would write in a PageController class public Details Update(PageId id, UserId user, [PostBody] Details body) and have your web framework automatically bind this to POST /page/update/{id}?user={user} with the appropriate serialization for PageId and UserId. And writing such a framework is easy: a couple hundred lines of code, with run-time type safety. In OCaml you have to understand the entire Eliom_parameter framework: https://ocsigen.org/eliom/4.2/api/server/Eliom_parameter https://ocsigen.org/eliom/4.2/api/server/Eliom_parameter Just think of the mental firepower needed to create that framework in the first place!