I'm surprised that better threading wasn't mentioned. As I understand it, OCaml has threads but parallelism isn't possible. That is, although you can have multiple threads, only one can execute at a time.
#5 of Build Tools and Runtime would cover that (encapsulating all global state into a runtime context). The ongoing multicore work at OCamlPro is taking this approach [1].
Erlang-style actors or lightweight processes would be my dream multicore scenario (no more monads for concurrent IO!), but that's likely too difficult to graft onto the current runtime, which is otherwise extremely well-done.
Also, I'd really like checked exceptions. The omission of checked exceptions has always struck me as strange, given the emphasis on compile-time checking. Considering how fast exceptions are in OCaml, it seems like a bit of a missed opportunity -- it is faster to use exceptions for control flow [2] than matching on variants (such as Option), which usually involve an extra allocation + dereference, IIRC.
[2] Odd as it may sound, it's encouraged to use exceptions as a control-flow construct in OCaml, but that is falling out of favor in modern code as far as I can tell.
Checked exceptions are possibly the worst feature of Java.
Now it may be a good idea to have an optional tool for checking exceptions -- in fact, one exists already, but I can't recall the name of it. But please don't make this mandatory unless you avoid the mistakes of Java.
Checked exceptions are possibly the worst feature of Java.
Why? I absolutely hate C++ codebases where all of a sudden you get yet another type of exception that was not documented, possibly because the exception was thrown ten functions down (up) the stack.
In Java, at the very least you know what to expect and can prepare for it. Unfortunately, even there people try to avoid exceptions by throwing classes that derive from RuntimeException.
Given that Java has no sum types, checked exceptions are really the next best thing.
That's a good point, and one I hadn't thought of. I wonder what it would take to wrap Unix.fork into a threading library to make threads more convenient?
There are apparently some such libraries already. I haven't used any, but they seem to all use process-level threading; there's no lightweight threading library (like Lwt) that uses real threads. Actually, I'm wrong; turns out it is possible to delegate Lwt threads to system threads: http://ocsigen.org/lwt/api/Lwt_preemptive. This model doesn't seem to allow communication between them, however.
Generally sensible, but a few things seem already fixed.
The duplicate argument thing already generates a warning (I wish warnings were enabled by default though):
# let f x x = x;;
Warning 27: unused variable x.
val f : 'a -> 'b -> 'b = <fun>
"Values of type string and bytestring are not mutable." In 4.02, strings can be made immutable (it's optional currently, to avoid breaking existing code).
Camlp4 is already on the way out, being replaced by Extension Points.
Rather than adding checked exceptions, I'd rather just see more use of variants in the standard library (3rd-party libraries mainly do this already). I don't see why checked exceptions should be faster - maybe this is just a compiler optimisation problem?
Most of these things seem nice but are actually a bad idea. Many of the rest are already implemented or in the process of being implemented. A couple of them are good ideas which are awkward to implement due to politics (e.g. the licence) or backwards compatibility.
Still, I enjoyed reading the post, and it is good to see people looking to improve OCaml and make suggestions.
I would be interested in seeing a similar list for Haskell. Like OCaml, it's a great language, but definitely has a few long-standing warts that could be fixed. (Among these, the many competing streaming libraries and cabal hell come to mind. Though maybe the first ain't so bad, since they are all pretty high-quality.)
I think there's a big difference between language issues (which are routinely being worked on) and community questions like the pipes libraries and cabal.
Probably the two biggest problems, both of which are currently being worked on, is a solution to true modules/modular dependencies (which is highly beneficial to fixing the community problem of cabal hell) and a solution to the "record problem" which is probably going to come in a new GHC extension which automatically allows for structural subtyping on public records via row types.
"Probably the two biggest problems, both of which are currently being worked on, is a solution to true modules/modular dependencies "
I'm looking forward to this. I have Haskell code in production and would greatly appreciate these features.
Is there a url where I can check/follow for the state of these efforts? Who is working on these? Thanks in advance.
Disclaimer: This sounds critical of Haskell and may seem like disagreeing with what you're saying, but neither of those are intended. I agree with what you said, and use Haskell almost exclusively for programming.
Completely agreed on the difference between language issues and community issues. From my perspective, the Haskell community does great with language issues. With GHC 7.10 we'll have AMP and OverloadedRecordFields, and I've seen a ton of work go into getting GHC ready for something like Backpack (throughout this summer). I think eventually these things will be solidified into a Haskell2018 or something, at which point a lot of the issues with Haskell-the-language that I've encountered when writing industry-style code will be gone. And I think that's great, and points to a bright future for Haskell; I don't think any other language has ever made me excited by the pace of progress in the language itself. (Some may say that in other languages you don't need to be excited by the next compiler release because the current one is good enough, but all languages have warts and deficiencies -- I think the GHC devs and the Haskell community do a great job finding these and trying to find clever and principled ways of improving them. See OverloadedRecordFields.)
However, I think community questions like the pipes libraries , cabal hell (to a lesser extent -- I think this is partially a tech problem that Backpack-like modules will solve), problems with the Prelude, etc, are an even bigger issue. Sadly, I think those issues are actually much harder. For instance, in the Haskell community, we constantly have issues with Strings being the default datatype for text, because Strings are just [Char]. This is a pretty terrible model for text most of the time (not all of the time, but most of the time). However, changing this requires not only changing the base library, but also hundreds of other libraries on Hackage. Some of these are likely unmaintained, and would break and stay broken. There are many ways in which I think the Haskell Prelude could be fixed (String -> Text conversion, fmap -> map and mappend -> ++ renaming, getting rid of confusing Control.* and Data.* heirarchies, etc), but the undertaking is gargantuan, because any of these changes would affect hundreds of libraries (some now unmaintained) in the ecosystem. And because of this, I've seen no plans or proposals on how Haskell should handle these sorts of changes.
Fixing Prelude is just one of the more difficult "community questions". We have a proliferation of very similar but competing libraries, which in some ways is good (people have choice, different libraries can make different design decisions, etc), but in some ways is bad (if I write a library that does streaming, I have to make a pipes version and a conduit version, or choose one). Again, Backpack modules will help here. Same goes for Haskell-to-Javascript compilers. However, with many of these, I think time will tell -- we have multiple options that are constantly evolving because we haven't really figured out the "right" way to do things yet, and eventually things will settle down.
As always, some of the hardest problems in programming are not actually coding problems, but people problems. Hopefully we can find a way going forward to find solutions to those as well.
I think you're bringing up some great points! I'm really excited for the opportunities improving the language will bring to give leverage to community issues. That said, I think a lot of community issues are also just about education.
String choice is a particular point for me. I think it's great that we have three string types as they each have their own tradeoffs. That said, I don't think the particular choice of Stringy type made is often driven by the tradeoffs as much as by convenience or partial information.
I really think it often does boil down to the Prelude design. Solving the momentum issues there is a huge problem.
I think this is pretty intentionally avoided most of the time since it clutters up the continuation-passing semantics of the code... With `return` you have a much more complex base language. Especially since you can achieve it using monads.
Monads don't (quite) short-circuit the later computations unlike a return statement. Anyway, I'd like to see an example of writing this using monads, since it makes the idea more concrete.
I can write a continuation monad in Haskell which would. Something like
{-# LANGUAGE DeriveFunctor #-}
module Return where
import Control.Monad
newtype Cont r a = Cont { runCont :: (a -> r) -> r } deriving Functor
instance Monad (Cont r) where
return a = Cont (\k -> k a)
m >>= f = Cont (\k -> runCont m (\a -> runCont (f a) k))
early :: r -> Cont r a
early a = Cont (\_ -> a)
ex :: Cont Integer [Integer]
ex = do
forM [1..] $ \i -> do
when (i == 33) (early i)
return i
Because it would make a lot of code less nested and simpler to understand. The most common case is:
if some_error_condition then
None
else (
// everything else in the function is nested
)
which would be replaced with:
if some_error_condition then
return None;
// everything else is not nested
The situation is particularly bad when you have to do a lot of small tests at the beginning of your function. Think permissions checks:
let user_id =
match user with Some id -> id
| None ->
printf "you need to specify a user\n";
return None in
if not (permitted user_id) then
printf "this user is not permitted\n";
return None;
// go ahead and return Some object
The alternative is to use exceptions, either hidden within the function (requiring a nested function), or demanding that the caller is careful about catching exceptions. Exceptions are an unsafe back door, when what's really needed is a return statement.
A style I sometimes see is people not indenting the normal code path of the match. So your example above would be written:
match user with
| None -> printf "you need to specify a user\n"; None
| Some user_id when not (permitted user_id) ->
printf "this user is not permitted\n"; None
| Some user_id ->
// go ahead and return Some object
Stay classy, downvoters. You know I've been a working OCaml programmer for the last 12 years, using the language most days, so I do know a bit about the pain points.
Unicode support is the biggest problem I have with OCaml, and it's something I noticed immediately as I tried to put in messages and keywords for a japanese friend.
It may not be wise to break current behaviour of the char and string types, but their literals should at least support unicode escapes. One way to do this would be to bring in UCS-4/UTF-32 strings as offered by Camomile, and have some simple convention to allow UCS-4 strings to be mapped from what are UTF-8 literals in the code.
I prefer that OCaml doesn't support Unicode in the core language, and that it's left to Camomile to support.
One reason is that it's very hard to get it right -- see how complex Perl 5 is which is about the only language that does do the right thing. Another reason is that baking an incorrect Unicode into the language would be worse than the current situation. For examples of brokenness, see: all of Win32, Java, Ruby.
It would be good to remove the broken String.lowercase and String.uppercase functions from the stdlib though.
Edit: Although perhaps your problem was more to do with supporting arbitrary binary in the source files (which instead are encoded as ISO-8859-1). That would be something to fix.
Haskell has its Text library type. Which is a great example of how complex UTF done right can be... And how it can be done quite nicely as a lib. String literal overloading is pretty important (and library-level rare) though.