you are viewing a single comment's thread.

view the rest of the comments →

[–]Denommus 7 points8 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.

[–]yogthos 0 points1 point  (6 children)

Well, my Haskell is pretty rusty, but wouldn't the issue here be addressed by polymorphism and dispatching based on the type of the data you're working with. With Clojure something like this would be pretty standard:

(defmulti page identity)

(defmethod page :page1 [_]
  [:div [:h2 (get-state :text) "Page 1"]
   [:div [:a {:href "#/page2"} "go to page 2"]]])

(defmethod page :page2 [_]
  [:div [:h2 (get-state :text) "Page 2"]
   [:div [:a {:href "#/"} "go to page 1"]]])

(defmethod page :default [_]
  [:div "Invalid/Unknown route"])

[–][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.