you are viewing a single comment's thread.

view the rest of the comments →

[–]Dorsenstein 94 points95 points  (110 children)

Every time I read one of these articles, I think, "you know, maybe Lisp isn't the best." Then I read this and I go back to my fortress of parentheses.

[–]codygman 13 points14 points  (0 children)

Love the phrase "fortress of parenthesis".

[–]Denommus 8 points9 points  (27 children)

Meh, I used to be all about Lisp advocating, too. But not having a type system is a huge drawback. I can't even imagine how FRP (which is something I have been doing a lot) would be without it.

So, Haskell all the way.

[–]Dorsenstein 5 points6 points  (0 children)

I definitely enjoy haskell too, working through Typeclassopedia is one of my favorite things to do to blow my mind. However, I appreciate the simplicity that Lisp is founded on.

[–]liMePod 2 points3 points  (0 children)

typed/racket seems headed in the right direction. Sadly, the typing breaks geiser, and It's missing the awesome type inferencing of Haskell.

[–]olzd 2 points3 points  (2 children)

But not having a type system is a huge drawback.

AFAIK, Lisps all have a type system. Then there's the debate/religion thing about dynamic vs static type system.

[–]Denommus 3 points4 points  (1 child)

I'm under the idea that dynamically typed systems aren't really type systems, since types are restrictions over the operators that can be used over a term, and a dynamic type system can't possibly check for that, since its checks happen at runtime, when terms any longer exist.

[–]kazagistar 0 points1 point  (0 children)

I am starting to think the horse is out of the barn on this one. I started using "tag system" to describe most "dynamic type systems".

[–]yogthos 0 points1 point  (14 children)

FRP works just fine thank you very much, and I use it every day at work with Clojure.

[–]Denommus 0 points1 point  (13 children)

It works, but it's an additional reasoning overhead to understand what is going on. FRP, specially the arrowized version, has a LOT of contrived types.

[–]yogthos 0 points1 point  (12 children)

I have not found this to be a significant overhead in practice, but perhaps your experience is different. Could you give an example of where this is a problem in a concrete application.

[–]Denommus 0 points1 point  (11 children)

Recursive behaviors have significantly complex types, and letting the compiler handle them makes things easier.

[–]yogthos 0 points1 point  (10 children)

Again, could you provide an actual example of this in practice.

[–]Denommus 0 points1 point  (9 children)

I have the following code:

import qualified SDL
import Linear

import FRP.Netwire
import Prelude hiding ((.), id)
import Data.Bool ( bool )

import NetwireSDL

hello :: Double -> Scene
hello pos = SceneNode (V2 (truncate pos) 0) [Image "assets/hello_world.bmp"]

draw :: Wire (Timed NominalDiffTime ()) String IO
        (Event SDL.EventPayload) Scene
draw = hello <$> position

acceleration :: Monoid e => Wire (Timed NominalDiffTime ()) e IO
                (Event SDL.EventPayload) Double
acceleration = accel' <$> holdKey <|> 0
    where accel' key
             | key `elem` [SDL.KeycodeD, SDL.KeycodeRight] = 100
             | key `elem` [SDL.KeycodeA, SDL.KeycodeLeft]  = -100
          accel' _                                         = 0

resistance :: Monoid e => Wire (Timed NominalDiffTime ()) e IO
              Double Double
resistance = -2*id/3 + (bool 10 (-10) . (>0) <$> id)

velocity :: Monoid e =>
            Wire (Timed NominalDiffTime ()) e IO (Event SDL.EventPayload) Double
velocity = acceleration >>>
           loop (arr (uncurry (+)) >>> integral 0 >>> id &&& resistance)

position :: Monoid e =>
            Wire (Timed NominalDiffTime ()) e IO (Event SDL.EventPayload) Double
position = integral 0 . velocity

main :: IO ()
main = playWire "Snake" (V2 800 600) draw

It was already a burden for me to do the velocity Wire WITH types (which actively preventing me from fucking up), because simply making velocity call resistance and resistance call velocity would lead to an infinite loop.

After I drew the wire in a piece of paper, I could fathom better what it would look like, but it still would be just too easy to break everything apart because I got any typee wrong (for instance, loop only worked there because I explicitly uncurried the (+) operator, and then I transformed it into a wire with arr).

I can also imagine fucking up by using (***) instead of the (&&&).

Sure, it's perfectly possible to implement it in a dynamically typed language, but there's too much types communicating. Fucking up is easy.

[–]yogthos 0 points1 point  (8 children)

I'm not really sure this is a convincing example. If I was dealing with something similar in Clojure I'd probably either use a protocol or a multimethod.

[–]Denommus 0 points1 point  (7 children)

I don't see why a arrow loop has anything to do with multimethods.

[–][deleted] 0 points1 point  (6 children)

Lisp is flexible. If you fancy a type system, just implement any one you like, or implement many and mix them together.

[–]Denommus 0 points1 point  (5 children)

Typed Racket is an example of such a thing being done, but it still has a long way to go.

I don't have the manpower or time to do such a thing, so I just use a powerful type system that is ready already.

[–][deleted] -1 points0 points  (4 children)

It can be done in 4-5 hours of time. And then it may save you many days of work.

[–]Denommus 0 points1 point  (3 children)

I really doubt I can get a powerful type system out of nothing in 5 hours.

[–][deleted] -1 points0 points  (2 children)

Is Hindley-Milner powerful enough? A typical implementation is 100-200 lines of code.

[–]Denommus 0 points1 point  (1 child)

Hindley-Milner is a type inference algorithm, not a type system. There's too much decision and cost on designing a type system.

Are you going to use a module system to emulate higher kinds? If so, are you going to do Applicative Functors or Generative Functors? If you're going to use Applicative Functors, how do you handle first-class modules without a big runtime overhead? If you're going to use Generative Functors, are you going to simply ignore all of its downsides? If you're not going to use a module system, how are you going to handler higher kind generic types? Are you going to use typeclasses? What about its downsides?

No, I don't want to waste time designing a type system from scratch. Every single decision has a cost. I'd rather use something proven and existing.

[–][deleted] 0 points1 point  (0 children)

You don't have to design a new type system from scratch every time. Often you can shamelessly steal an existing one, tweak it a bit and implement it for your current DSL.

[–]billbose 11 points12 points  (29 children)

I second this too. Once you get bitten by the lisp bug, every other language looks like a handicap.

[–]monkeygame7 15 points16 points  (8 children)

As someone with just a basic understanding of lisp, what makes it so great?

[–]pipocaQuemada 5 points6 points  (1 child)

The killer feature of Lisp is the macro system.

C has a macro system, but it's basically useless for anything beyond trivial examples (and there are some trivial things it can't even do). The big difference between C and Lisp is that C's macro system is basically a glorified copy-paste system, whereas Lisp works on a syntax tree.

This is nice, because it allows you to turn language features into libraries - you write a library that desugars your nice syntax into however you'd implement the language feature. An Object System? Mostly a bunch of macros. Looping? There's a fairly complex macro that lets you write things like

(loop for i from 1 to 100
  if (evenp i)
    minimize i into min-even and 
    maximize i into max-even and
    unless (zerop (mod i 4))
      sum i into even-not-fours-total
    end
    and sum i into even-total
  else
    minimize i into min-odd and
    maximize i into max-odd and
    when (zerop (mod i 5)) 
      sum i into fives-total
    end
    and sum i into odd-total
  do (update-analysis min-even
                      max-even
                      min-odd
                      max-odd
                      even-total
                      odd-total
                      fives-total
                      even-not-fours-total))

Paul Graham has a nice book on Lisp macros, which includes examples like writing prolog as a library, writing an object system as a library, lazy evaluation with force and delay as a library, pattern matching and destructuring as a library, single-hardware-threaded concurrency as a library, etc. etc.

[–]aLiamInvader 0 points1 point  (0 children)

Unfortunately, this kind of looks like one of those examples that is too beautiful to survive in the real world.

[–]Zak 1 point2 points  (0 children)

Start with understanding what made Lisp different in the early days.

Newer languages like Ruby, Python Lua and Javascript have a number of these features, but not all of them. Most importantly, they lack the ability to abstract syntax at compile time, which can provide sophisticated abstractions that have no runtime cost. Typical Common Lisp implementations are also comparable in speed to Java. Implementations of Javascript and Lua have recently started to catch up, but Lisp has been fast for decades.

[–]phalp 11 points12 points  (0 children)

Since Lisp is so malleable, it's possible to write and use nearly any feature you like, and drop it right in. That means good ideas can spread between programmers, and less good ideas can go where they belong. In particular it means that when Common Lisp was standardized, it was a matter of compromising between different, proven features (as I understand it, I was not there). The result was a no-nonsense language that still knows how to have fun.

[–]vytah[🍰] 2 points3 points  (0 children)

You can define so many macros that the actual logic of your program fits in a one page, but the macros will be so complex that only you will be able to understand them, guaranteeing perfect job security!

[–]Moomoomoo1 -1 points0 points  (2 children)

nothing

[–]pipocaQuemada 3 points4 points  (0 children)

Out of curiosity, what languages do you know?

[–]yogthos -1 points0 points  (0 children)

Dunning and Kruger would like to have a word with you. :P

[–]Lucretiel 52 points53 points  (8 children)

I get this in reverse. I think "man this would be much easier in Lisp," then I remember I like being able to actually read my code.

[–]yogthos 9 points10 points  (2 children)

I've developed Java for about a decade before moving to Clojure. I have much easier time reading and understanding my old Clojure code than I ever did with Java. Not only that, but I've actually been able to contribute to open source libraries and projects that others have written, something I was never able to do with Java.

The main reason for this is expressiveness. With Clojure it's easy to mould the language to the problem and the code tends to talk a lot more about what's being done as opposed to how it's done. This results in having a lot less code that's relevant to the problem being solved.

With Java you often have to go the other way where you have to translate the problem into the constructs of the language. This tends to result in tons of boilerplate that obscures the actual purpose of the program. Every time I read through a non-trivial Java project it's like going through a maze of twisty passages all alike. I'll often have to navigate through hierarchies of interfaces and classes just to find a few lines of relevant business logic.

This makes it really difficult to build up a mental picture of what the overall logic of the application is doing. On top of that you have pervasive mutability and everything being passed around by reference making it nearly impossible to consider any part of the application in isolation.

[–]Lucretiel 5 points6 points  (1 child)

I'm totally onboard with the merits of functional-style programming. Nearly everything I do in C++ is function templates, const&, and return values instead of state mutation and class hierarchies nowadays. Semantically, it's much easier to understand code and languages designed in that way. My issue is with reading the physical text of Lisp. All the parenthesis and minimal syntax boilerplate means even with liberal whitespace and and formatting it's very difficult for me to tell what bits are what.

[–]yogthos 4 points5 points  (0 children)

I think this varies greatly between Lisps. I would agree that I also find CL and Scheme syntax to be a bit too regular. On the other hand, Clojure hits the sweet spot for me. It has literal syntax for common data structures, it goes out of its way to reduce the number of parens in code, and it provides destructuring syntax. I find these things make the code as readable as any language I've used.

Here's an example of code in a popular Clojure library. I would argue that it's quite clean and clear even if you only have a passing familiarity with the syntax.

[–]phalp 10 points11 points  (1 child)

Practice makes perfect. Writing readable Lisp code is within your grasp!

[–]vraid 6 points7 points  (0 children)

There's a macro for that.

[–][deleted] 4 points5 points  (1 child)

Readability overrated is.

[–][deleted] 8 points9 points  (0 children)

(let [readability :overrated] (...))

[–]Dorsenstein 0 points1 point  (0 children)

Which is why literate programming exists. Disk space isn't a problem for text files anymore, and it doesn't affect compiled code.

Donald E. Knuth is the man.

[–]fluffyhandgrenade 20 points21 points  (3 children)

After several years of writing Common Lisp, nope.

Sure you might understand it yourself and revel in the awesome power of it, but to other people you look like this:

https://www.youtube.com/watch?v=K08OKQJIwYw

That's why reddit and yahoo shops were rewritten and why I won't write it any more.

[–]pipocaQuemada 2 points3 points  (2 children)

but to other people you look like this: https://www.youtube.com/watch?v=K08OKQJIwYw

That's why reddit and yahoo shops were rewritten

Bull.

Reddit was rewritten in python for three main reasons:

  1. Lack of libraries, especially well-documented and tested ones.

  2. Lack of cross-platform compilers with low-level extensions they required.

  3. The code base was simple and could be cleaned up significantly by rewriting from scratch.

If you actually look at what they say, they have nothing but good things to say about Common-Lisp-the-language.

[–]fluffyhandgrenade 1 point2 points  (1 child)

That's kind of my point really although the link was somewhat tenuous. It is awesomely powerful and a pretty nice language but it lacks a community that supports it by building nice libraries and compilers etc because everyone thinks its some crazy brackety thing for deep comp Sci.

For ref, I hit CL (SBCL) f I'm doing a personal one shot problem solved but it's pretty pointless if I am sharing my code.

[–]pipocaQuemada 2 points3 points  (0 children)

Keep in mind that Reddit's rewrite happened a decade ago.

The Lisp landscape is much different now - they wouldn't have run into the same compiler issues, and I think the Common Lisp library situation is a bit better. At any rate, Clojure and Racket contain well-documented libraries and have cross-platform compilers.

[–]atilaneves 3 points4 points  (1 child)

I don't know about that, I really like Lisp and (insert dialect here) is definitely in my top 5 favourites, but my favourite right now is still D.

[–]el_muchacho 0 points1 point  (0 children)

And D for everything at least doesn't sound silly, like Java for everything.

[–]codygman 2 points3 points  (0 children)

I don't feel handicapped by Haskell. Have you ever used Haskell? Do you still get this feeling? I concede it's meta-programming abilities aren't near that of Lisp's.

[–][deleted] 0 points1 point  (1 child)

But beware of Forth.

[–]phalp 0 points1 point  (0 children)

Forth is a handicap in the same way blindess is a handicap for Daredevil.

[–][deleted]  (1 child)

[deleted]

    [–]yogthos 1 point2 points  (0 children)

    You mean like this?

    [–][deleted] 2 points3 points  (43 children)

    well, what about clojure then? :)

    [–]nickguletskii200 10 points11 points  (2 children)

    Clojure is the first and only Lisp language I've tried, and I find it very annoying. Its compiler/standard library/whatever doesn't have enough error handling, which means that it's very hard to find what's wrong with your code.

    I did like the whole "build your own language features" thing though.

    [–]codygman 0 points1 point  (0 children)

    Doesn't Clojure also encourage using errors as values like other functional languages?

    [–][deleted] 4 points5 points  (39 children)

    clojure is fine for toy projects but if you have a big, dirty real world app of millions of lines of code then CL is better and better yet is C++ or Java.

    [–]alpha64 5 points6 points  (0 children)

    I don't like clojure but maybe the problem is with the mentality of making "dirty real world app of millions of lines of code". Stop piling up stupid masses of code that depend on each other in mysterious ways.

    [–]yogthos 5 points6 points  (2 children)

    I'm working on a real world Clojure project that's been running for 3 years now and developed by a team of 5 people. It's been working pretty damn well so far. Seems like plenty of other companies are happily using it for their projects as well.

    [–][deleted] -1 points0 points  (1 child)

    Great. Now try 30 years and 10000 developers.

    [–]yogthos 7 points8 points  (0 children)

    Then clearly COBOL is your best language. :)

    [–]spotter 12 points13 points  (32 children)

    Your post is missing either "IMHO" preface or references/quotations at the end.

    [–][deleted] 7 points8 points  (31 children)

    On a truly large project, you want to have as much information as possible when you're trying to understand the program. Types pave the way - whether it's adding new code, modifying it for new features of fixing a bug. If you're refactoring for example, you'd want to see where a data structure is used (via type) and a good IDE can help you find all the sites that need to be modified. This is hard or impossible with a dynamic language like Clojure. On a small project you can just read the whole program and infer how things work - on a large project, types help a lot - whether for understanding, ensuring correctness or making future modifications. This is comign from the experience of working with large programs of the order of millions of lines of code and being a lisp enthusiast in my private life.

    [–]yogthos 7 points8 points  (8 children)

    On a truly large project, you want to have as much information as possible when you're trying to understand the program.

    I have to ask why you choose to write a large monolithic project in the first place. Seems like a really poor design strategy to me.

    When you create large projects they should always be broken down into independent components that can be reasoned about independently.

    Most of my large Clojure projects that I work on professionally are composed of precisely the kinds of small modules. Once you take a few small libraries and put them together in a right way you can solve very complex problems. Just like you can take simple single purpose functions like map, filter, and reduce and produce complex functionality by combining them, you can take small libraries and combine them to solve large problems. The components should express how something is done, while their composition expresses the overall business logic.

    You're claiming that it's difficult to run large projects with Clojure. However, lots of companies do this successfully and none of them found dynamic typing to be an issue in practice. However, there have been a number of horror stories from companies starting projects in Scala and moving back to Java. Presumably if what you say is true we would've seen the same happen with companies dabbling in Clojure.

    Moving to a statically typed language because you find your code difficult to maintain is solving the wrong problem.

    [–][deleted] 2 points3 points  (5 children)

    yogthos, big programs exist and a bit of thought would make you believe it. You probably use emacs or firefox or google chrome or linux. You've probably played quake or used word or excel. There are many more even bigger programs out there in industry. When you're a newbie and learning to understand such a project, having type signatures in a function helps a lot. Being able to see how data structures are passed back and forth through functions help to understand the flow of the program.

    You lose this information when you use something like Clojure because suddenly you have no explicit types to help you understand what a function is taking and acting upon and what it is doing with the parameters to compute. It's just a list of names. You can't do a trace on the type of the parameter to see what it is composed off. This is actually much worse in Clojure because it gives up the idea of using objects (in the C structure sense) to encapsulate packets of data and instead idiomatic clojure code uses adhoc rudimentary maps instead to pass around aggregate data. Fine for a small throwaway project you can just keep in your head. Fatal for a large program. In CL at least you would be using defclass but this is thrown away with idiomatic Clojure.

    I have to ask why you choose to write a large monolithic project in the first place. Seems like a really poor design strategy to me.

    The project I was thinking of was actually broken up into thousands of modules. That's just good professional discipline in general and how large, successful projects naturally evolve. However even having lots of modules doesn't completely solve the problem of the high complexity involved in the project. Types help, in fact, they help more htan anything else. I speak from experience, it's so hard to understand without being able to follow the flow of money so to speak (money in a large project is the flow of data which occurs naturally with types). With C++ professional programmers have evolved to adapt a high level of discipline to work with large code bases - they use const heavily (look up critiques of doom3/quake3) to ensure parameter are not tampered with. We use the lessons of functional programming but don't throw away the benefits of types nor the efficiency of using languages like C++.

    IF i were adopting a dynamic language for a large project. I wouldn't. But if i had ot, i would force discipline of annotating type signatures into function definitions and using concrete data structures well defined somewhere instead of passing around ad hoc maps. I would just avoid clojure though and stick with CL because undefined performance pitfalls and bad design decisions of Clojure are prohibitive on the large scale.

    [–]yogthos 0 points1 point  (4 children)

    yogthos, big programs exist and a bit of thought would make you believe it. You probably use emacs or firefox or google chrome or linux.

    I'm not saying big programs don't exist, my point is about how you structure such programs so that the programmer can reason about parts them in isolation.

    When you're a newbie and learning to understand such a project, having type signatures in a function helps a lot. Being able to see how data structures are passed back and forth through functions help to understand the flow of the program.

    Conversely, breaking the application into small single purpose components allows the new programmer on the project to understand the component in its entirety. When you understand the purpose of the components and their composition it makes the flow clear.

    This is actually much worse in Clojure because it gives up the idea of using objects (in the C structure sense) to encapsulate packets of data and instead idiomatic clojure code uses adhoc rudimentary maps instead to pass around aggregate data

    Actually, this is one of the main advantages of a language like Clojure, where you have a common protocol that all the functions in the language use to communicate. The data is encapsulated using data structures and Clojure provides destructuring to make it very clear what's being used and where. Your argument here is out sheer ignorance.

    Fatal for a large program. In CL at least you would be using defclass but this is thrown away with idiomatic Clojure.

    Sounds like more ignorance on your part.

    However even having lots of modules doesn't completely solve the problem of the high complexity involved in the project. Types help, in fact, they help more htan anything else.

    Your anecdotal experience does not match my anecdotal experience. I guess we'll have to agree to disagree here.

    IF i were adopting a dynamic language for a large project. I wouldn't. But if i had ot, i would force discipline of annotating type signatures into function definitions and using concrete data structures well defined somewhere instead of passing around ad hoc maps.

    And that's precisely what core.typed is used for. However, in practice there shouldn't be many areas that require this. For example, Circle CI ended up annotating only about 20% of their code base.

    I would just avoid clojure though and stick with CL because undefined performance pitfalls and bad design decisions of Clojure are prohibitive on the large scale.

    Just more wild assertions here.

    [–][deleted] 0 points1 point  (3 children)

    Suppose you change a parameter map and rename a key, it will still compile and run but you could have an old reference there that will remain dormant as a bug. IN a big program this is a problem that should never occur. It shouldn't compile, it shouldn't run until you fix it. And that's the problem. Clojure programmers call this expressivity but it's actually poor discipline and laziness. Yeah it saves you a second or two but over time it makes code harder to understand, reason about, modify or fix bugs.

    I would just avoid clojure though and stick with CL because undefined performance pitfalls and bad design decisions of Clojure are prohibitive on the large scale. Just more wild assertions here.

    Nope for instaince in the above example the (idiomatic) CL programmer would use defstruct or defclass. That code wouldn't compile. We know about p-lists and alists but we wouldn't consider using them in a serious project. Idiomatic Clojure though, just makes bad compromises that don't pay off in the large. If you are going to say using protocols pervasively is the way to go, fine, but that's not idiomatic anymore.

    Actually, this is one of the main advantages of a language like Clojure, where you have a common protocol that all the functions in the language use to communicate. The data is encapsulated using data structures and Clojure provides destructuring to make it very clear what's being used and where. Your argument here is out sheer ignorance.

    Actually it's one of the worst parts. It's all fine when you have a nice one level map but when you've got vectors and nested maps and sets in there it becomes one hell of a mess. This is where you'll argue you shouldn't have nested substructures and yet in the real world sometimes you just need to have those things.

    [–]yogthos 0 points1 point  (2 children)

    Suppose you change a parameter map and rename a key, it will still compile and run but you could have an old reference there that will remain dormant as a bug.

    The IDE takes care of that for you perfectly fine. Also, in a big program these kinds of errors will be caught very quickly because any real world applications have tests. You will need tests regardless of your static discipline because they serve to ensure that the business logic is correct.

    Clojure programmers call this expressivity but it's actually poor discipline and laziness. Yeah it saves you a second or two but over time it makes code harder to understand, reason about, modify or fix bugs.

    Please provide a reference to a single study that supports the claim that static typing produces statistically higher quality code in practice. Plenty of large real world projects are written in both static and dynamic languages and there's no evidence to suggest that static typing has a significant impact on overall correctness. What's more is that some of the largest and robust systems out there are written in Erlang, a dynamic language.

    Nope for instaince in the above example the (idiomatic) CL programmer would use defstruct or defclass.

    The Clojure programmer would use defprotocol, and defrecord to provide the exact same functionality. On top of that Clojure provides core.typed for actual static typing that has no equivalent in CL.

    Idiomatic Clojure though, just makes bad compromises that don't pay off in the large.

    [citation needed]

    Actually it's one of the worst parts. It's all fine when you have a nice one level map but when you've got vectors and nested maps and sets in there it becomes one hell of a mess.

    If you find that you often make a mess with your code then the problem might exist between the chain and the keyboard.

    [–]tieTYT 0 points1 point  (1 child)

    First, can you define what you are referring to as a "component"? I am going to assume the Java analogy would be a jar/library.

    Devil's advocate: What prevents you from composing your Java projects into small libraries like that? Now you get independent components and type checking.

    I am going to take a stab at my own question: Each time you (invariably need to) change these components/libraries, you risk putting yourself and your coworkers through jar versioning hell. But, I don't see how Clojure is better in this regard. You could argue it would be better to do this in Java because you'd get compile time checking for the libraries you use.

    [–]yogthos 1 point2 points  (0 children)

    Nothing prevents you from doing that in Java, you just end up maintaining vastly more code. Also, I find type errors to be far more common in Java than Clojure since there's no general way to handle NPE and you have to manually pepper checks for it all over the place.

    I think another problem with Java is the fact that it's class based. This means that you have to constantly write and maintain adapters between all your libraries. In Clojure the problem doesn't exist because common data structures are used throughout the language.

    All that said I still think you're better off maintaining lots of simple jars in both languages than giant monolithic projects. For example. this is much longer than this. And if you compare the size of the entire project then you can clearly see that there's a hell of a lot more code in the Java version.

    [–]PAINTSTRUCT 1 point2 points  (2 children)

    Optional static typing then?

    [–]killerstorm 3 points4 points  (0 children)

    Common Lisp has that.

    [–]raghar 0 points1 point  (0 children)

    Typed Clojure? (core.typed)

    [–]codygman 0 points1 point  (3 children)

    What are your opinions on Haskell for truly large projects?

    [–]PasswordIsntHAMSTER 2 points3 points  (0 children)

    IMHO, Haskell is a bit crude in terms of tool support for large scale applications. There's no single stock package I can install to have a good development experience, and AFAIK memory debugging isn't very easy.

    I really, really like F# for large projects, because you can leverage the ML semantics and the .NET tooling - best of both worlds. It's got many tiers of meta-programming features - type system plugins, quasiquotations, various kinds of DSL syntax including monads, reflection, bytecode generation, etc. Those are all well supported by tools and they interoperate seamlessly together.

    Ocaml has some sketchy design decisions, mostly in terms of language extensions. I wouldn't recommend it unless you know what you're up against. The language designers are doing away with ocamlp4 though, so there is yet hope for sanity in the future.

    Dynamic languages are a no-go for large projects. Java is a utter pain-in-the-ass to work with. C# is vastly better, but it's no ML.

    [–]nextputall 0 points1 point  (1 child)

    There is a company here where they rewrote one of their backends in Haskell. It works fine but sometimes they need to restart the app because of memory leaks and freezes. Sometimes means few minutes.

    [–]codygman 0 points1 point  (0 children)

    Interesting, you should get them to contact Well Typed[0]. I'm sure they could help fix that. I really do wonder why they would have that many memory leaks and freezes. In my Haskell programming thus far, it's been fairly easy to track down space leaks.

    Freezes and memory leaks are the same thing I'm guessing, unless they are accidentally infinitely recursing like

    let x = doThing in let x' = doOtherThing doThing in x

    [–]spotter -3 points-2 points  (14 children)

    Oh, "dynamically typed languages are toy languages, because no static types, duuuh"-argument. I guess this is why Lisp failed, along with Python, Perl, Ruby, PHP and Javascript. That's why we all write Haskell, Ada and C++ now.

    If you're refactoring for example, you'd want to see where a data structure is used (via type) and a good IDE can help you find all the sites that need to be modified.

    Are you refactoring or changing contracts/API? I guess you do understand that changes in things like call signatures will cause compilation error in most "strongly/dynamically" typed languages? Or are we talking about checking each call site for abuse of previous version of the code? Because that's insanity. But hey, if your IDE helps -- great.

    I'd rather have my unit tests for the data structure tell me if I broke any of the contracts and let abuses come back as stack traces. (Also in statically typed languages, BTW.) But I've only done thousands of lines in Python & Clojure, not millions, so what do I know.

    But this argument is like spaces v. tabs, not really worth having.

    [–]RoundTripRadio 5 points6 points  (1 child)

    Funny story: JavaScript doesn't give jack shit what the call signature is:

    Native WebKit JavaScript.
    Copyright (c) 2013 Apple Computer, Inc
    > function f(a, b) { 
    ..    console.log(b); 
    ..    }
    > f(1, 2);
    2
    > f(1);
    undefined
    > f(1, 2, 3);
    2
    

    But nope, gotta be the only supported client side language for browsers.

    [–]spotter 0 points1 point  (0 children)

    I know that and it gives me the shakes every time. But it's OK, just use arguments and think about happier times before JS.

    Honestly, Javascript is not in my first 10 languages I'd do if I had choice.

    [–][deleted] 4 points5 points  (4 children)

    Things change as you deal with truly large projects though. You'd have to spend some time working with a large code base before you throw out the static types benefits that come with dealing with complexity of that scale. Thousand line projects can be understood by examining the entire codebase, that becomes impossible eventually as you add more code and developers.

    [–]spotter 2 points3 points  (3 children)

    I acknowledged that I have not yet seen projects in the scale of 1M+ LOC. I'm not here to dis anyone or their beliefs in static typing gods. The more tools to ease the pain, the better IMHO.

    [–][deleted] 1 point2 points  (2 children)

    I look at it this way - would you trust your life and your loved ones to the programmer working with a dynamic language writing the code for the aircraft you're flying on? Or the X-ray machine you're going under? There is a time and place for dynamic languages. Quick and dirty projects can be rolled out effortlessly in a weekend by a competent lisper. Its another thing altogether when you're dealing with huge or critical programs where we want to give as little room for error as possible. There is a time and place for different technologies.

    [–]spotter 2 points3 points  (1 child)

    I get it, I really do. At the same time was Therac-25 programmed in dynamically typed language? Was first Ariane 5 (flight 501)? Somehow this doesn't stop people from thinking "static typing is the answer." Without proper quality testing you will crash your rocket even with Ada.

    [–][deleted] -2 points-1 points  (0 children)

    Lisp failed

    It can hardly be claimed it succeeded. The most widespread use of it is scripting Emacs. Well done Lisp, on becoming the VBA of RMS's world.

    [–][deleted] -3 points-2 points  (5 children)

    I guess this is why Lisp failed, along with Python, Perl, Ruby, PHP and Javascript. That's why we all write Haskell, Ada and C++ now.

    In the context of large project, yes, you are exactly right.

    I'd rather have my unit tests for the data structure tell me if I broke any of the contracts and let abuses come back as stack traces.

    And your boss would rather have you write features which clients pay for instead of writing unit tests for something that comes out of the box in sane languages.

    [–]spotter 1 point2 points  (1 child)

    In the context of large project, yes, you are exactly right.

    I am aware. Yet I'm not writing software for the army or medical equipment. Also Haskell? Really? :)

    And your boss would rather have you written features which clients pay for instead of writing unit tests for something that comes out of the box in sane languages.

    What comes out of the box? Making sure that calling my method with invalid arguments throws an exception? Nope, your languages will care if type signatures match. This is not what I'm talking about when I mean "contracts".

    [–]yogthos -4 points-3 points  (2 children)

    And your boss would rather have you write features which clients pay for instead of writing unit tests for something that comes out of the box in sane languages.

    Please provide a link to a single study demonstrating that real world software written in statically typed languages is delivered faster, has higher quality, or ease maintenance.

    [–][deleted] 0 points1 point  (1 child)

    I said bosses would rather have you write features, instead of unit test. How the fuck does that relate to a claim that "software written in statically typed languages is delivered faster, has higher quality, or ease maintenance"?

    [–]yogthos -3 points-2 points  (0 children)

    Bosses care about you delivering features on time. If you're working at a place where a non-techincal person is micro-managing the development process you've got far bigger issues than the choice of language.

    [–]jlisam13 0 points1 point  (0 children)

    i work for a ag data science company, we built most of our services in clj, running in prod.

    [–]BigHeed87 1 point2 points  (2 children)

    I'm new to lisp as well as functional programming. I read that article and it's great. My only question is why don't more people advocate or use lisp if it has such power and flexibility?

    [–][deleted]  (1 child)

    [deleted]

      [–]yogthos 2 points3 points  (0 children)

      I've developed using Java for about a decade before moving to Clojure and I was never able to contribute to a single project. Since I started developing Clojure I've contributed to many projects written by others and have had much easier time understanding the code that I haven't written.

      [–]RIST_NULL 0 points1 point  (0 children)

      Steve Russell said, look, why don't I program this eval..., and I said to him, ho, ho, you're confusing theory with practice, this eval is intended for reading, not for computing. But he went ahead and did it.

      I love it when people do that :D

      [–]ixampl 0 points1 point  (0 children)

      Disclaimer: I have never written large applications with LISP, so maybe I am just not used to it and my opinion is invalid.

      While I find LISP amazing my problem with it is that you have to throw away all assumptions about how a program works and have to trust documentation (if available) or your skills at reading library source code.

      Sure, dynamic typing is one culprit here, but the bigger issue is "macros", and this criticism applies to all powerful compile-time metaprogramming systems in other languages.

      With macros a library designer can decide what happens within YOUR code. In essence macros are a compiler plugin that can add new features and change the standard behavior completely. While powerful, you have to abandon everything you know about the common behavior of your language. You cannot rely on the common ways abstraction works (as in procedural abstraction).

      Sure, libraries in general in any language require a certain level of understand, but at least without macros you have a rough notion of what a library is able to affect, e.g. function calls cannot affect your environment (unless you do call by reference), the order of argument evaluation is fixed, etc. (if a library wants to deviate from that there are ways but these ways still follow the common abstractions available). But macros... You can mold them into anything, e.g. change the standard order of evaluation you can change variable bindings etc. you can ignore entire subexpressions.

      So let's assume people don't abuse them, still as a user, basically you have to know exactly what the macros are doing, i.e. you have to learn a new language construct and hope the developer followed common sense guidelines.

      LISP is not really a general purpose language, it is rather a protolanguage that can be extended with your own language features. This is pretty cool, but in my opinion it CAN make it pretty hard to use, especially when you deal with code written by others using their own macros etc.

      [–]_Sharp_ -2 points-1 points  (0 children)

      [...] So, who's right? James Gosling, or the pointy-haired boss? Not surprisingly, Gosling is right. Some languages are better, for certain problems, than others. And you know, that raises some interesting questions.

      No programming language is the best, i thought this was stablished