[HN Gopher] My thoughts on OCaml
___________________________________________________________________
My thoughts on OCaml
Author : ghuntley
Score : 150 points
Date : 2023-04-25 12:44 UTC (10 hours ago)
(HTM) web link (osa1.net)
(TXT) w3m dump (osa1.net)
| Miltnoid wrote:
| I used OCaml as my daily driver in grad school and my postdoc,
| and recommend my students use it. I think the main benefits of
| OCaml are:
|
| Functional Has mutable references Not lazy
|
| Haskell's type system is just better. But for large, complex
| systems that require performant code, it can be quite difficult
| to track the laziness, and sometimes life is just easier if I can
| use a mutable references.
|
| The author is completely correct that there's a big failure in
| the lack of a type-class-like system. This is why people ask so
| much for modular implicits. Core is also a decent standard
| library (though I don't know why they require sexp_of_t and
| t_of_sexp on like all their data structures).
|
| So I'd gladly use a different functional language with mutable
| references and strict evaluation. But there isn't any besides F#,
| and I have a Mac and don't personally want to figure out .NET on
| Mac.
| yawaramin wrote:
| I think looking at it as a 'failure' or 'deficiency' is the
| wrong mindset. OCaml makes a different tradeoff than Haskell.
| In exchange for not having ad-hoc polymorphism, OCaml's functor
| system allows much quicker compilation and easier-to-understand
| type errors. Like everything in tech, it's a tradeoff.
| ywei3410 wrote:
| They require `sexp_of_t` and `t_of_sexp` because of their
| workflow being based on them; such as expectation testing [1]
| and tools such as [2]. It's a less noisy alternative to json
| for many people.
|
| [1] https://blog.janestreet.com/testing-with-expectations/ [2]
| https://github.com/janestreet/sexp
| owenm wrote:
| Starting with F# on Mac is worlds away from what it was in the
| past, I bet you could be up and running in 5 minutes:
| brew install dotnet-sdk dotnet new console -lang F# (in
| your project directory)
|
| Start VS Code in that directory, install ionide via extensions,
| write code... dotnet run
| JaggerJo wrote:
| Exactly. And you can even pick between 3 IDEs
|
| - JetBrains Rider (best IMHO)
|
| - Visual Studio for Mac
|
| - VS Code with Ionide
| devmunchies wrote:
| Sublime text too with the fsautocomplete LSP server.
| frou_dh wrote:
| Knowing the virtually dead Standard ML is a curse when it comes
| to appreciating the aesthetics of OCaml. The two languages have
| similar syntax, but in just about every way they differ, OCaml
| syntax took the ugly road :(
| choeger wrote:
| Simply put, OCaml is very principled. There is a good reason for
| pretty much every design decision.
|
| The only thing one could argue is the lack of type classes, i.e.,
| overloading. (Calling it "interfaces" is somewhat wrong.)
| jorkadeen wrote:
| An alternative to OCaml is Flix (https://flix.dev/) which
| attempts to address some of the critiques in the post. For
| example:
|
| > Bad standard library
|
| A particular goal of Flix is to have a consistent standard
| library based on type classes.
|
| > Standard types are sometimes persistent, sometimes mutable.
| List, Map, and Set are persistent. Stack and Hashtbl are mutable.
|
| In Flix immutable types are named List, Set, Map, etc. and
| mutable collections are prefixed with Mut, e.g. MutSet, MutMap,
| etc. Mutable data types also carry a region.
|
| > Also, OCaml has no tracking of side-effects (like in Haskell)
|
| Flix has an effect system that tracks purity/impurity.
|
| (I am one of the developers of Flix)
| weatherlight wrote:
| Complete Feature List algebraic data types
| pattern matching first-class functions
| extensible records parametric polymorphism type
| classes higher-kinded types light-weight
| polymorphic effects type aliases Hindley-Milner
| type inference CSP-style concurrency buffered &
| unbuffered channels first-class datalog constraints
| polymorphic datalog predicates constraints with lattice
| semantics stratified negation interoperability
| with Java unboxed primitives keyword-based
| syntax redundancy checks monadic let\*
| expressions expressions holes compilation to
| JVM bytecode full tail call elimination core
| standard library parallel compiler architecture
| human friendly errors interactive mode Visual
| Studio Code support
|
| Nice!
| networked wrote:
| I have been keeping an eye on Flix. Thanks to you and rest of
| the Flix team for working on a bold but practical language. I
| wish you success. I think Flix is one of the most promising
| languages in the ML family. (Do you consider it an ML?) I love
| that it has embedded Datalog.
|
| I know it is pre-1.0, but I would still like to ask about
| adoption. Are there notable third-party open source projects in
| Flix yet? By "third-party" I mean created outside of Aarhus
| University and the University of Waterloo.
| jorkadeen wrote:
| Thanks for the kind words.
|
| Yes, I would definitely consider it a language in the ML-
| family. Its core is based on Hindley-Milner (like StandardML,
| OCaml, and Haskell) which I think is the hallmark of ML-
| family languages. Flix sits neatly between Ocaml and Haskell
| in that it allows side-effects (like Ocaml, unlike Haskell)
| but they are tracked by the effect system. Hence one can
| program in the spectrum between those two languages, but get
| strong guarantees from the compiler.
|
| In terms of adoption, we see a lot of people trying it out,
| and there are some packages out there already, but nothing
| too big yet. We recently added support for package
| management, so hopefully that will help the community grow.
| networked wrote:
| Nice to hear about the package management.
|
| Sorry, I wasn't clear. I meant to ask whether you
| considered Flix "an ML" as opposed to just being in the ML
| family. It's an ontological question, and not important, so
| don't sweat the answer. I am just curious how Flix
| developers see their project.
| ywei3410 wrote:
| I don't think this list is fair at all.
|
| > No standard and easy way of implementing interfaces
|
| Firstly another commenter has mentioned [1] modules as
| interfaces. There is also the object system [2] which I've seen
| used to great effect in similar scenarios.
|
| Typeclasses and modular implicits are fantastic - being a heavy
| user of Scala and Rust; but I've always found that they tend to
| be best when used as an alternative to macros, for derivation
| purposes.
|
| The interface and code split in OCaml is also really nice for
| value types and comes with no limitations; for years, we had
| issues in Scala because of the host platform, and in Rust
| exposing functions for your newtype can be oddly boilerplate
| heavy.
|
| > Bad standard library
|
| The OCaml standard library is (infamously) anemic and obviously
| is not as fully-featured as something like Java or Python, but
| since alternative libraries tend to be compatible with the
| bundled StdLib (both use the same `option` and `list` type), it
| means that having libraries dependent on different ones is not a
| huge issue. Learning resources, can be confusing though.
|
| I'm not sure why it's a problem certain data structures are
| mutable and some are persistent; the documentation is usually
| fairly clear and you usually have a performance characteristic in
| mind when selecting your data structure.
|
| Universal equality and hashes are indeed a little annoying (here
| an `Eq` typeclass would be great!) but I do understand the
| original decision to have them inside. At the very least, it's
| not pointer based equality and since records and most types don't
| have subtypes, you don't run into as many issues. You can of
| course use the equality methods on the modules themselves and
| since `Hashtbl` and co have functors, you aren't penalized for
| it.
|
| Strings are a problem-ish (what older language doesn't have
| string issues ha!), but there is utf-8 support now with [3].
|
| > but just two years ago it was common to use Makefiles to build
| OCaml projects
|
| It is true that OCaml has a unix-y flavour, but honestly I don't
| really understand the snipe at make. Sure there are alternatives,
| but make is bundled on most distros, has a quick startup time and
| a lot of people are familiar with it.
|
| More of the community is moving towards dune as well, which is
| pretty good; the docs could do with better indexing but there are
| some features such as promotion which are really nice.
|
| > was no standard way of doing compile-time metaprogramming
|
| I haven't used ppx much so I can't really comment here.
|
| Finally they've missed the biggest reasons to /use/ OCaml.
|
| [1] https://news.ycombinator.com/user?id=4ad [2]
| https://ocaml.org/docs/objects [3]
| https://v2.ocaml.org/api/Stdlib.String.html#utf_8
| boxed wrote:
| > Finally they've missed the biggest reasons to /use/ OCaml.
|
| You missed writing what they are :P
| xvilka wrote:
| Building, LSP server, and immutability are definitely better in
| 2023 than author describe in their article.
| nequo wrote:
| In what sense is immutability better? What changed?
| kthielen wrote:
| This piece focuses on complaints about ocaml, and the last half
| seems to just be syntax complaints (which I mostly ignore just
| because every language seems to have awkwardness in its syntax --
| certainly Haskell, a language otherwise held up as an example,
| can be shown to accept some really weird text).
|
| Good things about ocaml that I think should be mentioned are
| Hindley-Milner type inference (some might argue this, but it's a
| very effective system for inferring types), and also
| straightforward semantics (it's usually pretty easy to look at
| some code and make a reasonably accurate guess about what it will
| do on real hardware -- compare with Haskell).
|
| I'm sympathetic toward the initial complaints here, which I also
| think are the most substantive. Type classes and qualified types,
| as in Haskell, would be very useful in ocaml/ML. The examples
| offered (show_of_int, show_of_double, ...) are valid, and also
| the related complaint that dishonestly broad types like
| structural equality (where runtime errors are raised for cases
| where the types are actually not supported) also make a
| compelling case for qualified types in ML.
|
| I made this project ~10 years ago after making similar
| observations: https://github.com/morganstanley/hobbes
|
| Things like structural equality and ordering are user functions
| here rather than magic internal definitions (e.g. equality is
| defined as a type class, and type class instances can deconstruct
| algebraic types at compile time to decide how to implement
| instances). But evaluation is eager by default, so it's pretty
| easy to reason about performance. And data structures can be
| persisted to files and/or shared transparently in memory with
| C/C++ programs without translation, also very convenient for the
| places where this was used. Actually we built some very big and
| complicated time series databases with it, used in both pre and
| post trade settings where ~15% of daily US equity trades happen.
| So I think these observations are useful and have passed through
| some pretty significant real tests.
| Joker_vD wrote:
| > It also has the "dangling else" problem:
|
| God I hate this term because it's not even a _problem_. For a
| human reader, it 's pretty obvious that an "else" should belong
| to the closest "if"; and it's quite difficult to write a
| mechanical parser that would match "else" with the furthest "if"
| instead. So what is, exactly, the problem? That the grammar is
| technically ambiguous? Who cares? The authors of LR-generators?
| They don't, they have been resolving shift-reduce conflicts by
| default in favour of shifting for half a century already.
| manymanydots wrote:
| > No standard and easy way of implementing interfaces.
|
| > In Haskell, this is done with typeclasses ...
|
| > In OCaml there's no way to do this. I have to explicitly pass
| functions along with my values, maybe in a product type, or with
| a functor, or as an argument.
|
| Haskell is a mixed community / paradigm language too.
|
| I routinely program in "Braindead Haskell" (keep stuff simple and
| direct as much as possible). Funnily, in that programming style,
| using typeclasses for _all_ interface types is bad style.
|
| Instead, the Handle pattern [0] can be used. Which really is just
| shoveling functions in a product type. And imagine - it works
| worderfully.
|
| Everyone has to find their own style I guess.
|
| [0]: https://jaspervdj.be/posts/2018-03-08-handle-pattern.html
| Jeff_Brown wrote:
| > the Handle pattern
|
| I love this so much.
| KingOfCoders wrote:
| I found that one hilarious
|
| "The regex module uses global state: string_match runs a regex
| and sets some global state. "
| debugnik wrote:
| The builtin `str` regex library is obsolete anyway, most people
| use `re`[1], or sometimes bindings for pcre or Google's re2.
|
| [1]: https://ocaml.org/p/re/latest
| ronnier wrote:
| Why would a company use ocaml?
|
| I've worked in software now for most of my life, from small to
| the biggest companies. I've watched teams with senior developers
| use non mainstream languages to build services and just about
| every time those devs get board again, leave the team, leaving
| them stuck with this system that's hard to develop and different
| from the rest of the orgs, eventually resulting in a complete
| rewrite.
|
| At companies I try to be boring, not weird, and efficient. Which
| usually means for me, writing in a very mainstream language that
| is easy to hire for.
| kaba0 wrote:
| It won't really answer your question, but I found the Signals &
| Threads podcast fantastic, and it made me appreciate OCaml much
| more (it is produced by someone working at Jane Street).
|
| I personally found the MirageOS, build systems and language
| design episodes terrific.
| jlouis wrote:
| A language like ocaml excels when the input and output are
| program-like, and the problem can't be number-crunched by
| hardware. The typical example would be a compiler. Other
| examples would be model checkers, code generators for number
| crunching libraries, proof assistants, and so on. Complex
| problems, where you need to explore different avenues of
| approach quickly, and where symbolic manipulation is at the
| heart of the problem.
|
| Thus, ocaml is best used as a "kernel" or "module". The
| contextual part of your system uses the part written in ocaml
| as a service.
|
| But ocaml doesn't excel at problems which are common to a
| typical company. The people who are in the intersection of
| wanting to solve those problems, and ocaml developers are few.
| The same intersection with a more mainstream language is far
| larger.
|
| However, if you find yourself with a problem at which ocaml
| excels, chances are the mainstream languages ends up falling
| short very quickly, because they tend to lack the abstraction
| features necessary for correctly handling the complexity.
| riku_iki wrote:
| The question is if modern versions of popular languages
| (java, c#) caught up on ocaml's syntax sugar, or maybe this
| syntax sugar is trivially replaceable by some utility
| functions.
|
| Maybe you could bring any example of such ocaml
| functionality?
| the_af wrote:
| > _Why would a company use ocaml?_
|
| PG claims using Lisp was the "secret sauce" to his early
| success. Whether this is true or not, and whether this is still
| true for companies today, remains to be seen. But it's an
| argument _for_ things like OCaml.
| decafninja wrote:
| I'm sure some companies out there might find a competitive
| advantage for using exotic languages and tech stacks.
|
| But I get the impression the more common scenario is that the
| engineers were bored and given the chance, wanted to play with
| something new and shiny.
|
| It also depends on the company's prestige. A company like Jane
| Street using OCaml? Considering both their prestige and the
| amount they pay, I'm sure they will have no problems hiring.
| Nor will their alumni have problems getting hired elsewhere.
|
| Some no name startup or even some "innovation lab" inside a
| boring cludgy Fortune500? Yeah, no thanks.
| tikhonj wrote:
| I worked on a team using Haskell at Target for a few years
| and we had a _massively_ easier time hiring compared to the
| other data science /ML teams. I figure it came down to two
| things:
|
| 1. People are actively interested in using Haskell and
| working with others interested in Haskell/PL/FP/etc. More
| people want to do Haskell than there are Haskell jobs and
| Haskell openings get massive word-of-mouth advertising.
|
| 2. It's a way to show that we're willing to do something
| different and interesting. Everybody _says_ they are, but how
| do you actually demonstrate it? I remember one of the first
| great hires we had was an OR professor who didn 't even know
| about Haskell--but joined in part because what we were doing
| was different and innovative.
|
| If I ever end up starting my own company, I'm going to use
| Haskell because it's so much _easier_ to hire for it--
| completely opposite to the superficial "common knowledge"
| that guides executive decisions at large companies.
| felixyz wrote:
| Jane Street wasn't all that prestigious when they started
| using OCaml. The way they put it is "it was easier to find
| great programmers in the empty set of people who know OCaml
| than in the huge set of other programmers" (roughly). So, on
| the contrary, I think it might help boring cludgy company
| find quality talent.
|
| I think there is a fallacy being repeated in this thread,
| namely that the choice is between mainstream and weird, or
| between old-and-tested and new-and-shiny. No, that's not the
| choice. The choice is between tools and abstractions that are
| helpful and productive, and those that aren't. Some of the
| helpful and productive things are old, some are new, some are
| mainstream, some are niche.
|
| I don't fully understand how someone wants to spend their
| career in programming and not actively seek out what is
| helpful and productive, rather than what currently gets the
| most questions on Stack Overflow or has a big pool of people
| who put it on their CV. It's not like all languages are
| equally good. It's a wasteful approach.
| decafninja wrote:
| There's another aspect, which myself and a few others in
| this thread have mentioned - hireability. Both from the
| company's and employee's perspective.
|
| I'm pretty sure Jane Street would have paid well even when
| they were an obscure name.
|
| Will boring cludgy Fortune500 company or noname startup pay
| well for someone to come use an exotic tech stack that is
| used very rarely at any other companies? Likely not.
|
| Likewise, would an engineer well versed in some exotic
| language, and can land offers at top companies, come work
| for a boring cludgy Fortune500 company that is only willing
| to pay a fraction of what they can get elsewhere?
|
| Maybe if you've already made your fortune elsewhere and
| well on the road to FIRE, and are really just looking for
| interesting work regardless of pay.
| felixyz wrote:
| So are you saying that Jane Street more or less bribed
| people who didn't really care for OCaml to come work in a
| weird language, by offering a good salary? I find that
| far-fetched, and there are many other examples of
| companies attractive highly-competent people because they
| choose -- no, not exotic, weird, or niche tech, but
| _good_ tech, that aid people thinking more clearly and
| abstracting better.
|
| As in everything else, there is a trade-off involved here
| of course. And what is truly good and helpful is by no
| means obvious, and might take time to assess.
| decafninja wrote:
| "Bribed" is a crude way to describe it, but in a manner
| of speaking, yes?
|
| I think you might be a tad idealistic, and you might
| think I'm being a tad cynical. Maybe the truth is in the
| middle somewhere.
|
| But throwing enough money at someone can talk. And not
| throwing enough money at someone can also talk. Short of
| being asked to do something unethical or illegal, what's
| wrong with that?
|
| Can you honestly say all of the talented top engineers
| out there working for top paying companies are doing it
| for the pursuit of technological perfection, and aren't
| doing it at least partially for the good money?
|
| I would say the same thing about doctors - people all
| love to say you should not get into medicine for money,
| but can we honestly say money isn't at least a factor in
| whether someone decides to pursue a MD?
| felixyz wrote:
| > I think you might be a tad idealistic
|
| Agreed :)
| yawaramin wrote:
| Ron Minsky of Jane Street has explicitly said, many
| times, that their choice of OCaml _attracted_ great
| developers who wanted to work with it.
| lallysingh wrote:
| .. that's a complaint for all new languages. You don't know
| which ones will stick until you try.
|
| Code in a strongly-typed functional language is easier to
| maintain because of the features of the language. The
| functional part gives great abstraction, and the strongly-typed
| part gives enough structure to make the whole thing tractable.
| rashkov wrote:
| Absolutely agreed. I'm lucky to work on a ReasonML front-end
| codebase and the type system and immutability are a real
| pleasure to work with. The strong typing lets you code
| against the compiler, and there are many instances of "if it
| compiles it works" that crop up throughout development.
| There's no fear of refactoring code that you didn't write,
| because the compiler will warn you about consequences on the
| far end of the codebase that you didn't even know existed.
|
| As a result of all of that, the app feels very solid to use
| and has comparatively few runtime errors.
| giraffe_lady wrote:
| Very cool to hear. I've been using rescript some recently
| and was hoping for exactly that sort of benefit.
|
| I have a mixed opinion of ocaml itself having used it
| professionally a moderate amount. But one thing I can't
| deny is that ocaml programs seem to "age" better than any
| other languages, even other strongly typed languages.
| anthk wrote:
| MirageOS and microkernels.
| yafbum wrote:
| Back when I tried it, there seemed to be a few verticals where
| Ocaml abstractions made Ocaml very productive to work with.
|
| Once a colleague and I did a programming race between two
| languages, to see how long it would take to implement a small
| cryptanalysis program using Ocaml vs C++. I wrote it in a day,
| and it took my desk neighbor who was doing the same in C++ a
| couple of weeks.
|
| What made the difference: - There were a lot of things I didn't
| have to worry about. For example, I was working with nontrivial
| structures but didn't have to write any boilerplate functions
| for deep equality or lex ordering. - Immutable structures with
| GC made it much more direct to translate the math into a
| working program. Ditto with guaranteed tail recursion
| optimization. - The match syntax caught many issues at compile
| time by forcing me to reason about every case. As a result I
| had many less runtime bugs to deal with, while my colleague
| needed to troubleshoot memory and correctness issues half the
| time.
|
| Ymmv. This was for research and didn't end up in production
| use. The C++ implementation was also much "closer to the metal"
| so was able to wring out more performance. Still the difference
| in initial velocity was striking and there could've been a case
| that this would've been a better choice based on dev velocity
| in that vertical.
| substation13 wrote:
| > Why would a company use ocaml?
|
| Why _wouldn 't_ a company use Brainfuck?
| vrglvrglvrgl wrote:
| [dead]
| JaggerJo wrote:
| I think you should give F# a shot instead.
|
| 1. No standard and easy way of implementing interfaces
|
| No problem in F#, you have interfaces, abstract classed, ...
|
| 2. Bad standard library
|
| In F# you have access to the full .NET standard library and
| ecosystem. There are also quite a lot of libraries that are
| especially designed to take advantage of F# (SQL libs for
| example).
|
| 3. Syntax problems
|
| 3.1 OCaml doesn't have a single-line comment syntax.
|
| It's `//` for single line and `(* comment *)` for n line comments
| in F#.
|
| 3.2 It has for and while, but no break and continue. So you use
| exceptions with a try inside the loop for continue, and outside
| for break.
|
| Same in F#, use recursion.
|
| Generally F# solves some of the issues but definitely not all of
| them. Some are just in the nature of the Standard ML syntax I
| guess.
|
| 4. Rest of the package is also not that good
|
| - NuGet is a decent/good package manager. - Easy to install a
| complete dev env. - sane build system (MSBuild) - Great out of
| the box support in JetBrains Rider/ Visual Studio (for Mac/
| Windows) or via a VS Code plugin (Ionide).
| veec_cas_tant wrote:
| Wanted to add my experience here: after writing C# code most of
| my professional life, F# is a breath of fresh air. The type
| system makes so many issues and annoyances I've lived with in
| C# just disappear. Much like Rust, the compiler is capable of
| providing so much more value when the type system is so
| expressive. You can bang out a lot of code quite quickly and be
| pretty darn sure it will work just because it compiles.
| daxfohl wrote:
| > NuGet is a decent/good package manager
|
| I remember F# designed its own package manager (paket) because
| of NuGet's deficiencies. I haven't been on that ecosystem for a
| while now. Have things changed? (My memory is that paket added
| friction and I eventually dropped it for nuget on my personal
| projects).
| ecshafer wrote:
| F# is such a great language.
|
| The fact Microsoft seems to pretend it doesn't exist is
| bewildering. Surely the .Net ecosystem can have two languages
| being promoted.
| pjmlp wrote:
| Before F# came into VS 2010, we already had C#, VB, C++/CLI
| (nee Managed C++), IronPython, IronRuby, and all the third
| parties targeting the CLR.
|
| It is really not having a clue what purpose they want for F#,
| I bet they have repented to ship it on VS2010.
|
| First it was for libraries only, then during the VS Express
| days it was for Web development, now they are trying to pivot
| it into data analysis and ML, while DevDiv manages to bring
| Guido out of retirement and finally manages to pursuade
| CPython core team about improving its performance.
| AlotOfReading wrote:
| F# was originally just kind of a passion project that MS
| insisted get productized to continue development. It's
| still very much an internal favorite within Microsoft
| Research and the Bing team, from my understanding.
| pjmlp wrote:
| Nowadays most of those folks are no longer at MSR, Don
| Syme has moved into Github and I doubt they are into F#.
| JaggerJo wrote:
| Don is still working on F#. The F# team at MS now is
| bigger than is was years ago. People from different
| companies and the community are collaborating on compiler
| and language features.
|
| F# is beyond microsoft, a lot of early adopters are still
| there.
| pjmlp wrote:
| Stand corrected on that one.
|
| What are the fruits of that increased team on the VS,
| VS4Mac and VSCode tooling, and .NET SDKs?
| pyjarrett wrote:
| > F# is such a great language.
|
| I very much feel this, I found it productive, but it feels
| like a complete ghost town. I'm considering swapping to Go
| over using .NET for tooling/scripting.
| JaggerJo wrote:
| Really? Were you active in the slack/discord channels?
| pyjarrett wrote:
| I was poking around https://forums.fsharp.org/ and
| https://www.reddit.com/r/fsharp/
| hpstewed wrote:
| I use F# at work. Really love the language but the connection
| to the dotnet ecosystem is a curse as much as a blessing.
| Having to operate in a world of nulls and exceptions can
| reduce/nullify the amazing benefits of discriminated unions.
| Often you are back to relying on the due dilligence of other
| developers as a result.
|
| I use a lot of rust in my spare time. The runtime guarantees it
| offers make other languages feel like a house of cards in
| comparison, including F#. I coincidentally picked up an ocaml
| book yesterday seeking a more 'robust' alternative to F#.
| Unfortunately it seems as though ocaml is even more niche than
| F# (I doubt I'll be working for jane street any time soon)
| WorldMaker wrote:
| The C# compiler got a ton of new smarts around reference type
| nullability and almost all of the BCL (.NET standard library)
| got annotated for it. I don't know when F# will finally pick
| up all the new compile-time smarts for Nullable Reference
| Types, but it has been proposed and prototyped, at least [1].
|
| [1] https://github.com/fsharp/fslang-
| design/blob/main/RFCs/FS-10...
| ragnese wrote:
| > Having to operate in a world of nulls and exceptions can
| reduce/nullify the amazing benefits of discriminated unions.
|
| I've been saying this about Kotlin on the JVM for ages. Using
| a Java dependency is an absolute last resort for me for those
| same reasons you mention about .NET from F#.
| HdS84 wrote:
| I really wish to like F# but the line noise kills me every
| time. I would kill for a "visual basic" f# where the absurd
| terseness is replaced with a little less noise. E.g. instead of
| seq use sequence, instead of abstract one letter symbols use
| words.
| JackMorgan wrote:
| Perhaps you mean mix a little more noise into the dense
| signal? There's very little noise in F#, it's almost entirely
| dense signal.
|
| One good thing is the ability to bind new operators, so if
| you don't care to learn to read <> you could make it
| something else. Or you could have a local binding for
| Sequence so you don't have to use just Seq.
|
| It's also possible to avoid almost all operators if you don't
| like them. There's nothing wrong with using a limited pool as
| long as you can get done the job!
|
| I also prefer to name variables, functions, and parameters
| with nice clear names, and I've not found F# to get in the
| way of that.
| JaggerJo wrote:
| what noise?
|
| I get your example of 'seq { 1; 2; 3; }' but list, array are
| not even shortened.
| HdS84 wrote:
| Sorry, I literally forgot them. A lookup here refreshed my
| memory: https://learn.microsoft.com/en-
| us/dotnet/fsharp/language-ref...
| vips7L wrote:
| This is my exact issue with many "modern" languages. The code
| is not meant to be read later.
| amyjess wrote:
| OCaml is one of those languages (like Raku and Nim) that I really
| want to learn and do something in at some point but usually end
| up not bothering because it's easy for me to just start up new
| personal projects in Python like I'm used to.
| spqr233 wrote:
| I don't the author really gave Ocaml a chance. He argues that
| every language needs to have interfaces. I agree somewhat with
| that statement, but I think the truer statement is to say that
| every language needs to have some way of defining composition at
| a structural level.
|
| An interface allows you to pass different "structures" to the
| same function so long as they adhere to the same spec. In Ocaml
| this is accomplished through modules, functors, and module
| signatures. Ocaml's module signature can play the same exact role
| as interfaces in Haskell, Rust or F#.
|
| The module system is arguably more expressive than what can be
| accomplished to interfaces. To give an example, Ocaml suffers
| from the same problem as rust does with having two standard
| implementations of a async runtime. Just like rusts: tokio and
| async-std, ocaml has Lwt, Async, (and newly added to the mix
| Eio).
|
| Whereas in rust most libraries just implement one of these
| systems, and you'll have to use compiler directives to support
| both. Ocaml's module system means that you can describe the async
| runtime as a signature and make your entire library generic to
| the async runtime it runs on top of. Most of the well-used
| libraries do this, and so you don't have to worry too much about
| which runtime you decide to use.
|
| Clearly, a language that can do that must have in some place a
| system that can replace interfaces.
| boxed wrote:
| To me it seems he gave it a lot of chance in order to find this
| many problems.
|
| It does sound to me like you stopped reading after the first
| point. Maybe you didn't give the post a chance?
| WastingMyTime89 wrote:
| To be honest, anyone who used Ocaml understands immediately
| that the author gave it no chance and went looking for
| problems. It's obvious because the post lingers a lot about
| things which are not actually issues like type conversion but
| don't talk about the very real issues Ocaml has (opam is not
| great, dune is weird).
| hardwaregeek wrote:
| Why are these things not issues? I think it's totally valid
| for someone who doesn't understand OCaml to raise issues of
| usability or documentation. It's not just the issues of
| experts that are a concern. And it's very demoralizing to
| get a "you're holding it wrong" response to a complaint.
| WastingMyTime89 wrote:
| Because they are not issues on a day to day basis when
| you use the language.
|
| I used Ocaml for a paid internship some years ago. I was
| very much a beginner. Do you know how many time I felt at
| loss or annoyed by type conversion or the precedence
| rules for various part of the syntax? I never did.
|
| The truth is you never encounter the precedence rules
| when writing Ocaml normally using parentheses like a
| normal human being and you don't convert that much
| between exotic types. Most of the conversion you actually
| do are between the same types and most of the time you
| have to write converter anyway because there is logic
| involved. I worked professionally as a Java developer for
| a bit. I think I had to write more conversion code
| between weird classes then than I ever did in Ocaml.
|
| The issue is not telling people they are "holding it
| wrong". The issue is that this is not what's going to
| annoy you as an Ocaml beginner.
|
| The author of this article is not even an Ocaml beginner
| by the way. That's actually someone who used to work on
| the Haskell compiler.
| illiarian wrote:
| > Because they are not issues on a day to day basis when
| you use the language.
|
| They still are. You just _internalize_ them. Progammers
| are the world 's greatest masochists with the world's
| biggest Stockholm syndromes, and we don't like to admit
| it.
|
| For example, his pain points about syntax ambiguity are
| absolutely valid, and true. However, when you "use the
| language every day" you learn to avoid those constructs
| and extract them into separate functions for example
| because "that's how it's always done, and it's a nice
| little quirk of the language".
| WastingMyTime89 wrote:
| No, the syntax parts he criticises are not this kind
| (they are not ambiguous by the way). What's in the
| article is very unidiomatic OCaml incorrectly intended
| purposefully to make it look like it's doing something
| else than what it does.
|
| You don't write tuples without parentheses because it's
| weird, harder to read and no one does it. Same for the
| precedence rules of expression. You just use _begin_ and
| _end_ for blocks like you would use braces in C because
| you actually want to write a block. No one is just
| randomly writing nested code without them because well it
| doesn't work.
|
| It's not a quirk. The author is just complaining that you
| have to mark blocks like in every language. That's a
| weird thing to complain about.
|
| I agree that the lack of as-hoc polymorphism is a
| downside (I don't agree with how the article presents it
| but the article is disingenuous from the start anyway).
| Everyone would like to have modular implicit.
| illiarian wrote:
| > unidiomatic OCaml
|
| > You don't write tuples without parentheses
|
| > Same for the precedence rules of expression. You just
| use begin and end for blocks
|
| This is exactly what I wrote, but in a Stockholm-
| syndrome-y way.
|
| > The author is just complaining that you have to mark
| blocks like in every language.
|
| He points out a real ambiguity in the syntax where the
| parser literally parses the same-looking and same-
| behaving statements completely differently.
|
| Sure it's solved by "you just write begin-end" workaround
| that you internalise. Or it could be solved at the syntax
| level (like erlang and elixir)
| WastingMyTime89 wrote:
| > This is exactly what I wrote, but in a Stockholm-
| syndrome-y way.
|
| Do you complain about having to put braces around C
| expressions to not get unexpected behaviour from the
| compiler?
|
| > He points out a real ambiguity in the syntax where the
| parser literally parses the same-looking and same-
| behaving statements completely differently.
|
| No, he doesn't. If that what you got from the article,
| you were bamboozled. He points out that match, try and
| (;) have different precedence which is not ambiguous.
| Writing _begin_ and _end_ is not a workaround. That's how
| you mark blocks which is what he should have done
| because, well, he wants a block.
|
| Erlang has exactly the same issue if you write
| unidiomatic code by the way.
|
| That's unsurprising because "don't write code implicitly
| relying on precedence behaviour" is taught to virtually
| all beginners in most programming languages.
| lmm wrote:
| If you shouldn't write code that way then it should be a
| compilation error (or at the very least a warning) to
| write code that way.
| ModernMech wrote:
| > The issue is that this is not what's going to annoy you
| as an Ocaml beginner.
|
| Sure, but tbf, the post is about what annoys the author,
| not what would annoy beginners. As you say, the author is
| not a beginner, and so their problems wouldn't be
| beginner problems.
| WastingMyTime89 wrote:
| That's not the point I was making. They are not expert
| problems either. How often as an expert do you think you
| encounter precedence issues you were not encountering as
| a beginner?
| boxed wrote:
| It sounds like it's weird and annoying to convert a value
| to a string. That's pretty damning. Did I misunderstand?
| spqr233 wrote:
| It's more that only really strongly disagreed with that
| point. Frankly, there are some cons with using Ocaml. In
| practice, I think the library support for ocaml isn't
| completely there given the small community.
| maleldil wrote:
| > Ocaml's module system means that you can describe the async
| runtime as a signature and make your entire library generic to
| the async runtime it runs on top of
|
| Is this the program describing the interface it expects, and
| the compiler matching that with the implementations structure
| (e.g. Go interfaces, Python Protocols), or is it something
| someone declares and the libraries implement?
| yawaramin wrote:
| Mostly the former. The compiler infers the type of the
| implementation and ensures it conforms to the interface. But,
| you can _further_ restrict the interface 's type to hide
| details from the outside world.
| actionfromafar wrote:
| > Ocaml's module system means that you can describe the async
| runtime as a signature and make your entire library generic to
| the async runtime it runs on top of
|
| So ... can you ELI5 to me how that is different from how you
| can for instance compile a C program against different libc
| implementations?
| kajaktum wrote:
| I mean...you can technically have a function that take void*
| and return void* be the interface. But you wouldn't be able
| to represent things like iterator and chain them together
| without care. In C you would be tripping all over the slight
| details like forgetting to check errno or something.
| actionfromafar wrote:
| So, it's like C++20 then?
| hardwaregeek wrote:
| Politely, I feel like this is the issue with OCaml as a
| community. You gave a beautiful answer about programming
| language design and composition. But how does it feel to write
| the language? How can you print a user defined data type? From
| reading around, it seems like your options are: explicitly pass
| a print function for that specific type, use a third party
| library for printing, or (my "favorite") don't. Or how does it
| feel to write a function that takes a generic that can be
| printed, checked for equality, and read? Or to convert one type
| into another type?
|
| And we're still talking about semantics here. How does it feel
| to use the language server? How does it feel to read the code?
| To build the language? Ultimately that's what users judge a
| language on. Yes, Rust in some ways has a worse form of
| abstraction. It is global, not fully generic and doesn't allow
| for overloading. But it creates a user experience that is
| nicer. It lets a user print something and compare two variables
| and do type conversions without having to scratch their head,
| read a forum post and import a library.
| yodsanklai wrote:
| > How can you print a user defined data type?
|
| Pretty much the same way you'd do in Haskell or Rust: you add
| a `[@@deriving show]` directive to your datatype.
| ywei3410 wrote:
| > You gave a beautiful answer about programming language
|
| You do the same thing as in Rust, Scala or Haskell and derive
| the printer [1]. Then at the callsite, if you know the type
| then you do `T.show` to print it or `T.eq`. If you don't know
| the type, then you pass it in at the top level as a module
| and then do `T.show` or `T.eq`.
|
| > Or to convert one type into another type?
|
| If you want to convert a type, then you have a type that you
| want to convert from such as foo and bar, then you do
| `Foo.to_bar value`.
|
| We can keep going, but you can get the point.
|
| You _can't_ judge a language by doing what you want to do
| with one language in another. If I judge Rust by writing
| recursive data structures and complaining about performance
| and verbosity that's not particularly fair correct? I can't
| say that Dart is terrible for desktop because I can't use
| chrome developer tools on its canvas output and ignore it's
| hot-reloading server. I can't say Common Lisp code is
| unreadable because I don't have type annotations and ignore
| the REPL for introspection.
|
| [1] https://github.com/ocaml-ppx/ppx_deriving
| hardwaregeek wrote:
| Okay...so you have to add a third party library to print?
| Yes, recursive data structures are a pain in Rust (well,
| recursive multi-owner data structures). But that's like
| comparing changing your oil to opening your car door. We do
| one of these a lot more. And bear in mind, I had to find
| this answer by googling and reading a random forum post.
| That's some pretty poor documentation.
|
| And that's not talking about aesthetics. `T.show foo` or
| `Foo.to_bar value` is a lot of syntactic overhead for a
| rather common task.
|
| And again, these are the lesser concerns to the other ones
| I outlined. Reading the code, building the code, and
| understanding the compiler errors are the big ones.
| agumonkey wrote:
| People have to realize that printing is the least of your
| concern. Visible representation is not solving problem.
| ywei3410 wrote:
| Not really third-party; ocaml-ppx is something similar to
| `javax` in the Java space? But it is optional and doesn't
| come bundled with OCaml.
|
| Recursive structures are only rare in Rust _because_ they
| suck to write and have terrible performance
| characteristics in the language. In languages like
| Haskell, OCaml and Scala using tagless initial encodings
| for eDSL's [2] are really, really common.
|
| I don't write Rust like I write any semi-functional
| language with function composition because, well, it's
| _painful_ and not a good fit for the language.
|
| > `T.show foo` or `Foo.to_bar value` is a lot of
| syntactic overhead
|
| The `T.()` opens a scope so OCaml code generally looks
| like:
|
| ``` let foo = T.(if equal zero x then show x else "not
| here") ```
|
| for,
|
| ``` fn foo(t: T) -> String { if(t == T::ZERO) { t.show()
| } else { "not here".into() } } ```
|
| Even when talking about usage, each language community
| for better or worse has a predefined workflow in mind.
| Your questions and workflow are specific to a particular
| language; you don't, for example, ask about the REPL, the
| compile times, debugging macros, the cold-start of the
| compiler, whether hot-reloading is a thing, the debugger,
| how it interacts with perf, how to instrument an
| application. Presumably because the language you use or
| the programs that you make don't have those components as
| part of their main workflow.
|
| [1] https://github.com/ocaml-ppx [2]
| https://peddie.github.io/encodings/encodings-text.html
| hardwaregeek wrote:
| A lot of these things that you mentioned are valid flaws
| in Rust. I'm not going to dismiss them as not important
| or non-problems because they are genuine issues. Compile
| times suck. We could use hot reloading and a REPL.
| Debugging macros, instrumentation and perf stuff is good,
| but I assume it could be better. That's what I expect
| from a language community. Not constant denial about the
| state of usability. And it really does feel like many of
| the smaller language communities fall into this hive-mind
| where type signatures are valid documentation and who
| needs to use an editor other than emacs?
| yawaramin wrote:
| If you are referring to OCaml, many people in the
| community care a lot about writing good documentation so
| that it's not just the type signatures. In fact the OCaml
| website team have made amazing leaps and strides
| integrating package documentation directly in the
| website.
|
| And the experience using OCaml LSP Server with VSCode
| nowadays is honestly pretty good--type annotations
| displayed, error messages, go to definition, generating
| interface files. The core functionality is all there.
| hardwaregeek wrote:
| That's good to hear! I don't mean to be too negative. I'm
| genuinely glad that OCaml is making these strides. I'm
| hopeful that OCaml can find its equivalent of Esteban
| Kuber[1] and they can make the language a truly fun and
| friendly experience.
|
| [1]: https://www.youtube.com/watch?v=Z6X7Ada0ugE
| yawaramin wrote:
| You don't _need_ a third-party library to derive a
| pretty-printer for your types, it just makes it simpler.
| OCaml takes the approach that the core compiler doesn 't
| do any codegen, that is left to PPXs (basically,
| middlewares which transform code) which run before the
| compiler.
|
| You can write a pretty-printer yourself using the
| conventions of the `Format` module. Is it manual and
| annoying? Yeah. That's why the PPX exists. It's a
| tradeoff.
| yodsanklai wrote:
| > `T.show foo` or `Foo.to_bar value` is a lot of
| syntactic overhead for a rather common task.
|
| A lot of syntactic overhead sounds like a stretch. I
| suppose you could drop the `T` like you do in Haskell but
| I personally like my language to be a bit more explicit.
| It improves readability.
| pdhborges wrote:
| Scala is probably not a good example because if you need
| some quick and dirty printing you can override toString and
| keep the rest of the generic code intact. In OCaml, Rust
| and Haskell you would have to add type constraints in a
| bunch of places to make println! T.to_string and show work.
| v0idzer0 wrote:
| Came here to make this exact point. But I totally agree with
| all of his other complaints. It's a good list of the cons, but
| ignored the many pros
| [deleted]
| WastingMyTime89 wrote:
| The last paragraph show this article for what is is. That's an
| Haskell fan trying to discredit Ocaml and doing it very poorly.
|
| It takes a special kind of writer to write an incorrect sentence
| about the expression syntax while quoting the actual definition
| from the specification which contradicts what's written just
| after.
|
| I generally disagree about the complaint regarding the syntax. If
| you indent the code properly and use parentheses as you should
| and the language invites you to do, none of this is ambiguous.
| That's bad code not bad language design.
| boxed wrote:
| "the language invits you to do"? What does that mean? If it's
| the right thing to do it shouldn't be in invitation surely, but
| a syntax requirement?
| WastingMyTime89 wrote:
| > What does that mean?
|
| That's a best practice. Like always using braces in C or Java
| or not using ternary operators in hard to read way.
|
| The syntax is unambiguous but non obvious to read if you
| don't know precedence rules as it is for most languages. You
| can make it explicit using parentheses which you should.
|
| I personally agree that parentheses around tuples should be
| mandatory but it's fairly easy to just put them.
| boxed wrote:
| Those are examples of C and Java being _very bad_. So yea,
| I think my point stands.
| WastingMyTime89 wrote:
| I'm not sure I understand your point then because the
| code in the article is definitely _very bad_ OCaml which
| is what I have been trying to tell you from the start.
| Verdex wrote:
| I agree, but the languages that actually do this are in a
| hyper-minority, I feel. Nearly every language allows you to
| do the wrong thing and then only lets you discover this at
| runtime via exception OR sometimes even just by careful
| examination of subtly incorrect output.
|
| As such, this doesn't really feel like the best argument
| against ocaml (and I believe arguments against ocaml do exist
| and on a much more accessible level).
| omoikane wrote:
| > Int.abs can return a negative number.
|
| The documentation for abs[1] explains: "Warning. This may be
| negative if the argument is Int.min_int."
|
| I think that's just the nature of two's complement and not
| necessarily a fault of OCaml. C++ is also like that[2].
|
| [1] https://v2.ocaml.org/api/Int.html
|
| [2] https://gcc.godbolt.org/z/34b3GbvvT
| anderskaseorg wrote:
| In C and C++, abs(INT_MIN) is undefined behavior. (C17
| SS7.22.6.1: "The abs, labs, and llabs functions compute the
| absolute value of an integer j. If the result cannot be
| represented, the behavior is undefined." Footnote: "The
| absolute value of the most negative number cannot be
| represented in two's complement.")
| [deleted]
| nu11ptr wrote:
| I like OCaml, but if I were to start a new project with an ML I'd
| probably lean towards F# to take advantage of all the .NET libs
| lysecret wrote:
| Same, big fan of f#.
| JaggerJo wrote:
| We use F# in prod. Could not be happier.
| dimitrios1 wrote:
| Completely ignorant here:
|
| - I really want to do a project in an almost "pure"
| functional language. I tried with Elixir and Phoenix, and
| while they are certainly great, and I wouldn't mind using it
| again, Elixir, and subsequently Erlang didn't feel like FP a
| lot of times, it felt like the warty Elixir/Erlang way to do
| FP, so it didn't scratch that itch for me (but again, still a
| great experience overall)
|
| - is there a way to do this openly? I.E. with dotnetcore,
| running on linux containers, no windows what so ever, etc?
| Guessing yes but wanted to make sure from people who have
| actually done it
|
| - What exactly about the .NET libraries that has everyone so
| hyped up about? I remember from my C# .NET days, they always
| felt to be lacking.
| victorNicollet wrote:
| Today you can install the .NET runtime, as well as the .NET
| development tools, on both Windows and Linux. They handle
| F# as well as C# out of the box.
|
| You can `dotnet publish MyApp.fsproj` on either Windows or
| Linux, copy the resulting `publish/` folder to another
| machine with a different OS, and use `dotnet MyApp.dll` to
| run it. There are also ways to create containers, though I
| have never tried them for lack of an actual use case.
|
| As for libraries, .NET comes with a huge standard library,
| much larger than what you would find on a different
| language. Off the top of my head, here are a few things
| that come with .NET that would have been a third party
| library in other languages: text encodings, protocols (HTTP
| client, TLS, QUIC, SMTP), mainstream hashes
| (MD5/SHA1/SHA256), mainstream crypto
| (AES/DES/RSA/EC/ChaCha), serialization (JSON/ XML), weird
| formats (X509/ASN.1/tar/MIME), mainstream compression
| (deflate/brotli/zip), binary data manipulation (e.g.
| endianness conversion, AVX intrinsics, memory-mapping),
| runtime code generation, in-assembly resources,
| interoperability (COM, JavaScript, Objective C, SQL),
| concurrent collections, async...
|
| Besides, since C# is popular, many tools have .NET-
| compatible clients or libraries which can then be accessed
| from F#.
| JaggerJo wrote:
| + macOS
| klester79 wrote:
| [dead]
| mjdowney wrote:
| Related: https://twitter.com/zetalyrae/status/1639474931086901248
|
| My OCaml: let foo (x: bar): baz = (\*
| Is this quuxable? *) match is_quuxable x with
| | Yes -> (* Then, we have to ... \*)
|
| All the OCaml I see in the wild: qlet%bind f =
| 's 't 'a 'b (fun -> ((<_<')) ) [@@ppx_gov_backdoor
| (~b~e~p~i~s)]
| mcguire wrote:
| Hmm. I haven't seen the latter in OCaml as much. More the
| opposite; when I first used it, things like list length in the
| standard library weren't tail recursive. 8-|
|
| Now, Haskell and _shudder_ Scala _shudder,_ on the other hand.
| WastingMyTime89 wrote:
| Compared to Haskell, OCaml developers are generally not to
| fond of custom operators which shield you from some of the
| worst cases unless you are reading code an Haskeller wrote in
| OCaml which happens.
|
| For a long time, most of the users were either writing
| compilers or static analysers for low level languages and a
| fair share was very knowledgeable in C and system
| programming. It had a huge impact on what was seen as
| idiomatic. You can still find trace of it in for exemple the
| system library which looks more like the C stdlib than a
| modern standard library or how the compiler finds libraries.
|
| Generally the community used to have a very different
| "flavour" than the Haskell one (it was also very French to be
| fair). I think I t's less true nowadays. Still I would fall
| from my chair if I ever encounter someone working on the
| OCaml compiler writing a disparaging disingenuous post on
| Haskell. Meanwhile, we are here which reflects exactly my
| general experience with the Haskell community.
|
| Ppx are used more and more often however and it did make code
| harder to read.
| jimsimmons wrote:
| Functional programming community likes this stuff. They go into
| the classical mathematicians' trap of writing more and more
| general versions of something even though it's not necessary.
| yawaramin wrote:
| I don't think this is correlated to functional programming.
| Programmers in general tend to over-architect because we're
| often learning as we're going and sometimes it's more fun to
| use some cool concepts. Since design patterns started
| becoming en vogue back in the day, OOP codebases got filled
| with those patterns. Now we are left picking up the pieces
| with things like `AbstractProxyFactoryBean` or whatever. Same
| thing with JavaScript where that ecosystem is infamous for
| using tons and tons of npm packages to recreate simple
| functionality, e.g. 'left-pad a string'. Arcane mathematics-
| heavy FP code is just how that expresses in FP languages. And
| in fact, OCaml is actually an antidote to that (for the most
| part), because it deliberately offers a lower level of
| abstraction than Haskell. I frequently say that OCaml is like
| a cross between Haskell and Go.
| nequo wrote:
| I think this is partly because of the syntax.
|
| When we write human languages (that use a Latin script), we
| use punctuation to delineate the beginning and end of terms.
| In a language like C or Rust, terms are surrounded by commas,
| parentheses, angle brackets, and so on. In OCaml and Haskell,
| many things that there is syntax for in C-style languages are
| done as ordinary function calls, which separate terms by only
| whitespace.
|
| It is easier to read this (Rust): let value =
| some_long_ass_function_name(some_quirky_parameter,
| another_one); another_function(value)
|
| than this (Haskell): let value =
| someLongAssFunctionName someQuirkyParameter anotherOne
| in anotherFunction value
|
| or this (OCaml): let value =
| some_long_ass_function_name some_quirky_parameter another_one
| in another_function value
|
| Individual terms in camel case are difficult to read if they
| consist of more than a couple words. Consecutive terms in
| snake case are difficult to read because underscores and
| whitespace look alike visually.
|
| Haskell and OCaml's idiomatic solution is to use extremely
| terse names for arguments, type variables, and local
| variables, out of what seems to be syntactic necessity.
| golergka wrote:
| That's because of currying. If you want to partially apply
| parameters, in your example, with Haskell we would just
| have to do this:
| someLongAssFunctionName someQuirkyParameter
|
| And we get a function that accepts one parameter. But in
| Rust you would have to do this: move
| |another_one: i32| {
| some_long_ass_function_name(some_quirky_parameter,
| another_one) }
|
| Which is not easier to read.
| nequo wrote:
| It is true that currying looks comparatively awful in
| Rust. But that is not because Rust uses parentheses and
| commas. A hypothetical C-style language could use
| dedicated syntax for currying, like
| some_long_ass_function_name(some_quirky_parameter, ..)
|
| which would be more readable without giving up on
| punctuation.
| ackfoobar wrote:
| Scala does that, with `_` being a "hole in the
| expression". I wouldn't call it currying though.
| func(param1, _)
|
| Every once in a while you have to make some change to
| that expression. // you may want to write
| func(param1, func2(_)) // but you need to write the
| full lambda param2 => func(param1, func2(param2))
| oblio wrote:
| Ah, now I realized, you're right. The lack of visual
| distinction between different syntactic elements is what's
| killing me.
| actionfromafar wrote:
| The latter code looks _totally_ quuxable.
| gjm11 wrote:
| Nah, I think it's already been quuxed up.
| ubertaco wrote:
| > Another example is the sequencing syntax <expr> ; <expr> and
| productions with <expr> as the right-most symbol:
| let test1 b = if b then print_string "1"
| else print_string "2"; print_string "3"
|
| > Here print_string "3" is not a part of the if expression, so
| this function always prints "3".
|
| Well, yeah. Here's the equivalent Java code:
| void test1(bool b) { if (b) print_string("1");
| else print_string("2"); print_string("3"); }
|
| You've chosen to use a single-statement "else" form _without_
| "bracketing" it with `begin` and `end`: let
| test1 b = if b then print_string "1"
| else begin print_string "2";
| print_string "3" end
|
| ...which is equivalent to this Java code, where you _also_ would
| have to "bracket" the else-block: void
| test1(bool b) { if (b) print_string("1");
| else { print_string("2");
| print_string("3"); } }
|
| You could argue that Java (and C, and C++, and Javascript,
| and...) has the same "ambiguous parsing problem", then -- except
| that it doesn't, and the author is just thrown off by
| `begin`/`end` as block delimiters instead of curly braces, which
| might be a sign that they've also never seen Ruby before, or
| Elixir, or Pascal, or....
| yakubin wrote:
| _> You could argue that Java (and C, and C++, and Javascript,
| and...) has the same "ambiguous parsing problem", then --
| except that it doesn't_
|
| The author didn't claim there's any ambiguity in parsing, only
| ambiguity in reading as a human. And I would agree that all the
| languages you mentioned have the same problem. And this is a
| real problem as long as people use this form of if (which they
| do). New languages avoid this pitfall by mandating the use of
| braces, in return dropping the parens, and I think that's
| great.
| yodsanklai wrote:
| It's a non-issue IMHO. Just use parenthesis if you're unsure,
| then auto-format. You'll catch any issue that wan't caught by
| the typechecker by reading the diff.
| charlesetc wrote:
| Yeah auto format solves a lot of the syntax problems.
| Dangling else for instance is not at all ambiguous when you
| have automatic indentation.
| [deleted]
| electroly wrote:
| Agreed except for the part at the end: Java, C, C++, and
| JavaScript definitely _do_ have the "dangling else" problem!
| It's a manifestation of the same basic issue. Who is saying
| they don't? You just showed how they do! It is a real problem
| and they all have it. The takeaway here isn't that those
| languages _don 't_ have the issue. They do. It's just really
| common in language design, that's all; certainly not something
| to single out OCaml about. Apple's infamous "goto
| vulnerability" was directly caused by this language design
| issue in good old C.
|
| https://en.wikipedia.org/wiki/Dangling_else (just to show that
| this is something people commonly consider to be a problem with
| Java, C, C++, and JavaScript)
| ubertaco wrote:
| >Java, C, C++, and JavaScript definitely do have the
| "dangling else" problem! It's a manifestation of the same
| basic issue. Who is saying they don't? You just showed how
| they do! It is a real problem and they all have it.
|
| Yeah, that's actually exactly my point: it's not some new
| weird novel horrible broken parsing problem that only OCaml
| has; rather, it's a common parsing rule that many languages
| (including Java, C, C++, and JavaScript -- and yes, OCaml)
| have.
| Arnavion wrote:
| Now keep reading on to literally the next sentence.
|
| The article's point is that the `if-else` behavior is
| inconsistent with `match`. And based on the part about double
| quotes in comments, the parser seems to be fucked up something
| fierce in other ways too.
| atbpaca wrote:
| I learned OCaml two decades ago. It was great to learning
| algorithmics but I already had the feeling the language was old
| and awkward. Today there is Scala, Haskell, Rust, F# etc.
| yawaramin wrote:
| All of which are, incidentally, either directly or indirectly
| inspired by OCaml or its ancestor ML :-)
| [deleted]
| s-zeng wrote:
| A significant amount of these issues are solved by sticking to
| Core, Dune, and avoiding imperative blocks
| anthk wrote:
| Ocaml5 is multithreaded. MLdonkey would run much better. Compared
| to the classical Amule, a monstruosity writen in C++, at least it
| doesn't leak memory like some youngster with a constipation.
| 4ad wrote:
| > No standard and easy way of implementing interfaces
|
| Article -> trash.
|
| Apparently the author doesn't know about OCaml modules, which is
| _the_ defining feature of ML-derived languages.
| koprulusector wrote:
| Can you elaborate for us newer OCaml users? My first reaction
| to this critique/point is the author must want mli, right?
| [deleted]
| sgeisenh wrote:
| Especially if modular implicits land, you get the best of both
| functions and typeclasses.
|
| Pretty weird way to start the post.
| wk_end wrote:
| That's a big "if". The author themselves mentions modular
| implicits and points out that they haven't been updated in
| something like five years. I don't think modular implicits
| are going to happen in Ocaml in our lifetimes.
| arc-in-space wrote:
| Someone mind explaining how these help? I'm unfamiliar with
| OCaml, but looking at the documentation of modules, I don't
| understand what they have to do with interfaces in the OOP
| sense(EDIT: or, rather, ad-hoc polymorphism), which I'm pretty
| sure is what the author meant.
| Verdex wrote:
| To be fair, ocaml doesn't have something _exactly_ like c#
| /java interfaces. Given some problem that you might want to
| solve with a c#/java interface, you can also solve it with
| ocaml modules by flipping the coding structure on it's head.
| I'm not going to try to explain exactly what this looks like
| because I actually don't really like that structure of
| coding, so I don't feel like I'm going to do it justice.
|
| BUT ocaml also has row polymorphism and polymorphic
| invariants, which you can use to get something much directly
| closer to c#/java interfaces. The major difference is that
| with ocaml it's structural typing and with c#/java it's
| nominal typing. However, at a high level you can write the
| code in a very similar structural pattern as with what you
| get in c#/java.
| WastingMyTime89 wrote:
| > I'm pretty sure is what the author meant.
|
| Clearly not considering he mentions typeclass in the article.
|
| Module signatures are similar to interface and parametrized
| modules allow you to do what you would do with abstract
| class: you define a signature and then write a module using
| these functions. Parametrized modules are more or less a
| generalization of functions which goes from module to module.
| arc-in-space wrote:
| Sorry, I initially wrote 'interfaces as in OOP' to
| distinguish them from module interfaces, but had 'ad-hoc
| polymorphism' in mind. 'interfaces' is much too overloaded
| of a term.
| WastingMyTime89 wrote:
| Ocaml has no ad-hoc polymorphism but you can use
| parametrised modules to write generic code which you will
| have to specialise at some point. That brings you ninety
| percents of the way in term of what ad-hoc polymorphism
| is used for. It's more cumbersome but I prefer it to the
| complexity involved by what Scala does for exemple.
| ywei3410 wrote:
| Would you not consider the object system to be ad-hoc
| polymorphism?
| octachron wrote:
| The OCaml object system is still parametric polymorphism
| (trying to pass itself as subtyping polymorphism through
| row variables) rather than ad-hoc polymorphism: there are
| still no functions that only work on a finite subset of
| types. In other words, a function may work on any objects
| that has a method `m`, but you cannot have the same
| method that has different implementation for `int` and
| `float`.
| WastingMyTime89 wrote:
| It uses structural typing not ad-hoc polymorphism. Plus
| it's rarely used and it used to give hard to parse error
| messages when you made type mistakes. I don't really see
| it as an adequate replacement for ad-hoc polymorphism.
| Errancer wrote:
| The point as I understand is that interfaces in the OOP sense
| try to mimic what modules do. Therefore OCaml not having them
| is not a serious accusation since everything you try to
| accomplish with interfaces / design patters you can and
| should be able to do with modules. Here is the article
| explaining the concept:
| https://www.pathsensitive.com/2023/03/modules-matter-most-
| fo... but I have to note that I never worked in OCaml so this
| is just my theoretical understanding.
| sparkie wrote:
| I can kind of understand the author's complaint. A problem with
| OCaml is there are several different ways to achieve the same
| thing, and they're not compatible.
|
| For example, you can do OOP-like interfaces/abstract classes
| with `class virtual`, but classes and modules don't mix
| together nicely. Your virtual class cannot abstract over
| modules, nor do functors abstract over classes.
|
| So you end up either using modules/functors pervasively, or
| using objects pervasively. It's like there's two separate
| languages competing for use.
|
| Using modules seems to be more common, but it overshadows some
| features of OCaml's object system which are pretty unique to it
| and useful: row polymorphism and functional objects.
|
| Obviously, functors have a clear performance advantage because
| they're expanded at compile time, and objects have the vtable
| overhead.
|
| I think it would be interesting to see a variant of OCaml which
| goes all-in on the object side of things. Strip away the
| modules and functors and a provide standard library based
| around functional objects.
| networked wrote:
| The author brings up modules and functors. A more charitable
| interpretation is that they want ad hoc polymorphism but are
| handed parametric polymorphism instead.
| Verdex wrote:
| Yeah, I'm not sure I understand the author's complaint. OCaml
| has modules, associated types, anonymous modules, GADTs,
| polymorphic variants, and row polymorphism. All of which are
| kind of like interfaces.
|
| As far as I can tell, the complaint is maybe that ocaml doesn't
| have something exactly like C#/Java interfaces (which I think
| is a fair assessment). So maybe the author is lamenting that
| they can't just rewrite their java code in every language?
|
| Although, they also mention rust traits. Which at first blush
| look _very_ similar to c# /java interfaces (at least closer
| than anything ocaml has). However, at least in my experience,
| rust traits don't quite fulfill the same niche as c#/java
| interfaces because rust cares a lot about when you can figure
| out the size of your objects. So every time I tried to use them
| like I would a C# interface, I wound up regretting the
| decision.
|
| Ultimately, this article feels like a superficial "I don't like
| ocaml" blurb. There is a lot to be warry of with respect to
| ocaml. Although, the fact that ocaml still exists as a niche
| language with a lot of very significant maintenance and active
| development (effects + multithread recently landed), shows that
| the author did not pay sufficient attention.
| aseipp wrote:
| The author is plenty aware of them, I can assure you (I used to
| work with him on the Glasgow Haskell Compiler; most Haskell
| programmers are plenty familiar with the ML module system, it's
| a highly coveted feature by many.)
|
| Anyway, what they're referring to has been rehashed a billion
| times already in the relevant communities. ML functors being
| good has little to do with it; even with functors and modules,
| you're required to actually pass them around and construct them
| explicitly and use it at call sites. If you want a Set for
| Strings, you make an instance of the Set functor with the
| proper implementation of the signature which specifies the Set
| ordering (or whatever, assuming that's the signature needed),
| but then you still have to _use_ the functor explicitly and
| pass it around. You can have more than one, for good reason, so
| making this explicit usage implicit is tricky. In contrast, in
| Haskell, there is one ordering for each (nominal) type, so
| there is no need to refer to specific instance of `Set String`
| or whatever to define the ordering. `Set` uses `Ord`, which has
| an unambiguous implementation. It is resolved implicitly for
| you.
|
| A lot of people just call this an "interface." Hell, even I do
| that. It's just a generalized term for "ad hoc polymorphism"
| where you don't need to guide instance resolution, I guess.
|
| The author even quite literally (in like, the next sentence)
| says that modular implicits would help this issue -- it would
| allow many uses of an instantiated module to become more
| implicit (e.g. uses of something like "turn this thing into a
| string for debugging" without having a menagerie of individual
| string_to_ functions). But they are not yet upstream. Even
| then, implicits have their own tradeoffs, and the design
| spectrum here has no clear winner. Even Haskell has adopted
| Backpack, which takes a different spin on the whole thing using
| mixin-modules (no functors), but tries to keep the
| characteristic power of functors available in a languages where
| typeclasses reign.
|
| Much of this is also relevant in the design of theorem proving
| communities, e.g. Lean4, which heavily relies on implicit
| instance resolution in a similar form to modular implicits; yet
| it doesn't have modules in the ML sense. They really want this
| approach though, because in math you really do have lots of
| instances you might refer to by name. But in programming it's
| not always the same; implicit resolution of ad hoc instances is
| often very useful.
|
| It's not really a weird or unusual complaint, it's been
| rehashed to death, but maybe it could be less ambiguous.
| felixyz wrote:
| Great comment. Thanks!
| yawaramin wrote:
| When writing about a language which has a well-known feature
| called 'interfaces', it behooves the writer to not use
| 'interfaces' to mean 'ad-hoc polymorphism' to avoid
| confusion. Not doing so led to the predictable confusion.
| cies wrote:
| ReasonML and ReScript are a (more or less the same) new syntax on
| top of OCaml. ReScript only targets JS, while ReasonML targets
| both JS and the native archs OCaml supports. Facebook and
| Bloomberg are using ReScript internally, afaik. Messenger.com is
| written in it. Facebook also maintains React bindings to
| ReScript.
|
| https://rescript-lang.org/
|
| https://reasonml.github.io/
| Taikonerd wrote:
| Is ReasonML dead? Their last blog post is from August 2018...
| https://reasonml.github.io/blog/
| cies wrote:
| Quieted down, but I depend on projects with worst graphs:
|
| https://github.com/reasonml/reason/graphs/contributors
| illiarian wrote:
| The rescript-reason split killed both IMO. ReasonML was a very
| tiny language to begin with. Splitting it into two split the
| already tiny language and community into two minuscule ones.
|
| And for no good reason (pun not intended).
|
| And they were doing quite well, and influencing OCaml in good
| and meaningful ways (like improving error reporting).
| giraffe_lady wrote:
| Rescript is completely excellent imo. I have used ocaml for
| years and while I like it more than most languages it's also
| weird and frustrating sometimes. Rescript uses the best parts
| of ocaml (type system & exhaustive pattern match, first class
| modules) to patch over the worst parts of js. It's almost
| alarming how effective it is to combine these two gnarly
| hostile languages like this.
|
| I also work in typescript a lot and it's the obvious
| comparison/competitor there. While it has objectively "won"
| it's so so much worse than rescript. Its type system is much
| more complex, untrustworthy inference, and ultimately is
| _still_ unsound lol. Ocaml 's type system is truly a wonder.
| Extremely practically helpful, complex enough to express
| anything without becoming a full blown language in its own
| right like eg typescript and haskell.
|
| Rescript compiles to very reasonable js and writing bindings is
| not particularly exhausting once you get the hang of it. I've
| been using it for a number of projects including completely
| irresponsible cases like deno fresh and it's been wonderful.
| What typescript could and should have been.
| crop_rotation wrote:
| The biggest problem I have with Ocaml is not the language, but
| the tooling and the libraries. The library ecosystem is not even
| remotely comparable to go or even rust. Jane street core is
| mentioned as a good option, but that library is the most popular
| undocumented library I have ever seen. There is not even some
| normal default structure every library follows (e.g go
| io.Reader).
|
| Compare this to languages with supposedly "worse" type systems
| like Java, which has a library for everything and high quality
| well documented libraries like guava and apache commons. I find
| it fine to use ocaml for a random weekend whim project, but
| wouldn't touch it with a 10 feet pole for professional work.
| illiarian wrote:
| > like Java, which has a library for everything and high
| quality well documented libraries
|
| Unless it's a library that has been around for a while and is
| probably hosted at Apache.
|
| There was a marked shift in 2010s in expectations, and now we
| expect libraries and code to be somewhat well-documented. For
| older libraries... well, it's often "here's a dump of
| autogenerated reference files with maybe a blob of prefacing
| text at the top. Good luck."
| oslac wrote:
| It is for these (tooling / library) reasons it is almost
| impossible to recommend it for any project, almost like Nix in
| that regard for me.
| mcguire wrote:
| " _Lack of widely used operations such as popcount for integer
| types, unicode character operations._ "
|
| popcount is widely used?
| yodsanklai wrote:
| 1. To print values, nowadays you just derive pretty printers
| automatically.
|
| 2. Syntax? yes, it's not C-like... you'll get used to it in a few
| days.
|
| 3. Lack of interface? would be helpful to see a concrete example
| of what the author finds limiting. You have modules, functors,
| includes, objects, first-order modules... Personally, I find that
| plain modules are simple and go a long way.
|
| It's not perfect, but I find that nowadays Core/Dune/Opam/Merlin
| gives you a very decent developer experience.
| the_af wrote:
| > _2. Syntax? yes, it 's not C-like... you'll get used to it in
| a few days_
|
| Since the author mentions Haskell as a positive example
| multiple times, I don't think a lack of C-like syntax is the
| problem for them here.
|
| Also, the author claims:
|
| > _Since 2013 I've had the chance to use OCaml a few times in
| different jobs, and I got frustrated and disappointed every
| time I had to use it._
|
| So it's safe to assume "in a few days" their dislike for OCaml
| syntax won't go away.
|
| For the record, I don't think syntax is a big issue.
| yodsanklai wrote:
| Incidentally, I find Haskell syntax more confusing,
| specifically the semantic indentation. But maybe I'm like the
| author, I don't use Haskell regularly enough so that the
| syntax sinks in.
| mattgreenrocks wrote:
| Been using Haskell full-time for a year or so now, and
| using it part-time a few years ago for a side project. The
| reliance on indentation is nice and light but definitely
| bites you every now and then.
| reikonomusha wrote:
| Coalton [1] is an OCaml-like dialect of ML: it's strictly
| evaluated, has imperative features, and doesn't force a purity-
| based development philosophy. However, Coalton has S-expression
| syntax and integrates into Common Lisp. It works, and is used "in
| production" by its developers, but it's still being developed
| into a 1.0 product.
|
| Coalton made a lot of design decisions to work away from issues
| that this post describes.
|
| - Coalton dispensed with ML's module system of structures,
| signatures, and functors. No doubt a beautiful concept, and
| sometimes satisfying to write, but almost always unsatisfying to
| use due to how explicit one tends to need to be. Like the author
| suggests, the "interface-style" of type classes or traits has
| always felt more intuitive, despite losing the ability to have
| multiple implementations of an interface for a single type.
| (However, I've come to appreciate that different interfaces
| _should require different types_ --as opposed to one type having
| many interfaces.) Coalton uses type classes.
|
| - Coalton has S-expression syntax. No indent rules. No precedence
| rules. No expression delimiters. All of the "tradition" of ML
| syntax is dispensed with into something that is a _lot_ easier to
| read and write for complex programs... yes, provided you use
| ParEdit or similar
|
| - It should be no surprise that with S-expressions, you get Lisp-
| style macros. Macros in Coalton are just Common Lisp macros. No
| separate pre-processors. No additional language semantics to deal
| with. Arbitrary compile-time computation and/or codegen,
| completely integrated.
|
| - Because it's built on Common Lisp, Coalton gets something few
| (if any?) other ML-derivatives get: Truly incremental and
| interactive development. You can re-compile types, functions,
| etc. and try them out immediately. There's no separate
| "interpreter mode"; just a Lisp REPL.
|
| Coalton's language features are still settling (e.g., records are
| being implemented) and the standard library [2] is still
| evolving. However, it's been used for "serious" applications,
| like implementing a compiler module for quantum programs [3].
|
| [1] https://github.com/coalton-lang/coalton
|
| [2] https://coalton-lang.github.io/reference/
|
| [3] https://coalton-lang.github.io/20220906-quantum-compiler/
| justinpombrio wrote:
| > Coalton [1] is an OCaml-like dialect of ML: it's strictly
| evaluated, has imperative features, and doesn't force a purity-
| based development philosophy.
|
| OCaml is also strictly evaluated, has imperative features (e.g.
| ref), and doesn't force a purity-based development philosophy.
|
| What makes you call Coalton a "dialect of ML"? ML's module
| system is its defining feature, beyond that there's really not
| much unique about it (by design). Would you call any language
| with algebraic data types a "dialect of ML"?
|
| EDIT: I'm picking on this one sentence, but the rest of your
| comment is a good overview of the language, thanks.
| reikonomusha wrote:
| That's why I said it's "OCaml-like" followed by a colon: to
| highlight the similarities.
|
| As for why it's an ML dialect, maybe (maybe not) it's more
| fair to call it "ML-inspired". Nonetheless, it's similar
| enough to the ML family to be understood by ML programmers.
| But yes, one of the major pillars of ML--the module system--
| is conspicuously absent in favor of Haskell-like type
| classes.
| wk_end wrote:
| This looks excellent! I have a soft-spot for the elegance of
| Lisp but can't live without a robust type system. A few
| questions I didn't see immediate answers to on the GH page
| (sorry if I missed something/could've found those answers if I
| digged a little deeper):
|
| 1. Beyond the typeclasses you mentioned, how powerful is the
| type system beyond standard HM stuff? Are there
| GADTs/existential types? Any other interesting features?
|
| 2. The lack of records sounds like it might be a deal-breaker
| for current use. Is that as bad as it sounds? Is it something
| that is expected to be addressed soon? Are there any other
| large omissions?
|
| 3. How does it integrate into the existing Common Lisp
| ecosystem, if at all? Is there any friction there?
|
| 4. On that note: in the REPL example it looks like you need to
| wrap your expressions with (coalton ...), which feels like it'd
| get frustrating. Is there any way around that? I guess even a
| reader macro would help...but still wouldn't be ideal.
|
| 5. Is Coalton backed by any kind of corporate funding? And
| (assuming you're involved in Coalton in a professional
| capacity) ...you guys hiring?
| reikonomusha wrote:
| 1. There are functional dependencies and multi-parameter type
| classes. No GADTs (yet?) or existential types (yet?).
|
| 2. Records are being implemented but it's far from being a
| dealbreaker, in the sense that thousands of production lines
| of Coalton have been built without needing them. It's just
| not ergonomic to shuffle around and pattern match against
| record-like data. With pattern matching in function
| arguments, things are at least easier.
|
| 3. All Coalton functions compile to Lisp functions. There's a
| guide that describes what is promised about interop
| (https://github.com/coalton-
| lang/coalton/blob/main/docs/coalt...).
|
| 4. Since Coalton functions are Lisp functions, you can call
| (unconstrained) functions directly. However there's a lot of
| room for improvement. There's an open issue to make a
| dedicated Coalton REPL that can show types and not require
| COALTON to be typed.
|
| 5. Coalton is developed as an open source project to build
| tools at HRL Laboratories, so it does receive sponsorship.
| Yes, HRL is hiring. Feel free to send me an email to the
| address in my profile.
|
| As seems to be the case with any "serious" programming
| language, there's always another thing to do--seemingly
| perpetually--to make said language useful enough for
| "serious" use. :)
| networked wrote:
| > Coalton has S-expression syntax. No indent rules. No
| precedence rules. No expression delimiters.
|
| This is a great selling point for a language in the ML family.
| Programmers can't abuse infix operators when there are none.
| :-)
|
| Besides this, Coalton looks appealing. A highly interactive
| statically typed language is something I have thought of. I am
| sure I am not the only one. It is also a good sign that you are
| using it as it is being developed.
| aaws11 wrote:
| Almost all modern statically typed languages have closures,
| higher-order functions/methods, lazy streams, and combinators
| that run efficiently. Persistent/immutable data structures can be
| implemented even in C.
|
| Also, OCaml has no tracking of side-effects (like in Haskell),
| and the language and the standard library have lots of features
| and functions with mutation, such as the array update syntax,
| mutable record fields, Hashtbl, and the regex module.
|
| The only thing that makes OCaml more "functional" than e.g. Dart,
| Java, or Rust is that it supports tail calls. While having tail
| calls is important for functional programming, I would happily
| give up on tail calls if that means not having the problems
| listed above.
|
| When you mix imperative and functional styles tail calls become
| less important. For example, I don't have to implement a stream
| map function in Dart with a tail call to map the rest of the
| stream, I can just use a while or for loop.
|
| In my opinion there is no reason to use OCaml in a new project in
| 2023.
| hencq wrote:
| You might want to make it clearer that this is a literal quote
| from the last paragraph of the article. Are you posting this
| because you agree/disagree?
| kajaktum wrote:
| Any language that have exceptions are trash imo. It is clearly a
| relic of the old day. It works for CPUs but not people.
| iLoveOncall wrote:
| [flagged]
___________________________________________________________________
(page generated 2023-04-25 23:00 UTC)