Haskell WTF



  • I know I talk up Haskell quite a lot. I have become very disillusioned about a pretty big Haskell project, though. It bills itself as the "type safe web app server", but as I have found out, because of a series of poor design decisions, it is extremely difficult to customize.

    Basically, the issue is that in Haskell, there is a fairly common pattern for creating algorithms that vary by type. It is closely related to the template pattern you might use in OO, with the same basic pros and cons. You create a type class for your algorithm, and give it a default implementation in terms of other class methods:

    class MyAlgorithm obj where
        algorithm :: obj -> IO () 
        algorithm = defaultAlgorithm
    
        parse :: obj -> IO obj
        parse = defaultParse
    
        etc :: obj -> IO ()
        etc = defaultEtc
    
    defaultAlgorithm :: MyAlgorithm obj => obj -> IO ()
    defaultAlgorithm = parse >>= etc
    
    defaultParse :: obj -> IO obj
    defaultParse = error "i'm not implementing a parser for an architecture example"
    
    defaultEtc :: obj -> IO ()
    defaultEtc = error "ditto"
    

    So, since default methods can be overriden, you can go ahead and change your etc and so on, if my choices are inappropriate for your type. Super! This pattern is a good thing, as long as you're given the ability to change the things that can be sensibly changed. It sucks when you can't. Because there is no inheritance, it can mean you have to fork the library.

    Now, the framework I'm bitching about uses this pattern. But seems to miss very strategically located methods. It is almost as if the author wants you to fork the source. Once you fork the source, you find out that the internals are all stringly typed. You read that right. The internals pass around byte-strings and text-strings at critical interfaces. And the library changes quickly, so if you take on the burden of maintaining a fork, you will spend a lot of time debugging things that ought to have been type errors instead of silent runtime errors.

    No, wait, it seems as though the author wants to make it expensive to do anything beyond whats in the tutorial book, so you purchase expensive consulting from the company that just "hired" him. What the fucking fuck. Fuckers.


  • BINNED

    @Captain said:

    Now, the framework I'm bitching about uses this pattern. But seems to miss very strategically located methods. It is almost as if the author wants you to fork the source. Once you fork the source, you find out that the internals are all stringly typed. You read that right. The internals pass around byte-strings and text-strings at critical interfaces

    Which completely negates the primary advantage of using Haskell. At the very least, they should be using templates. Is there any particular reason you're withholding names to protect the guilty?


  • Discourse touched me in a no-no place

    @Captain said:

    The internals pass around byte-strings and text-strings at critical interfaces.

    What sort of critical interfaces are we talking here? Dealing with the web — well, any network protocol — means a lot of dealing with strings.

    Filed under: byte sequences are a kind of string, if you squint…



  • @dkf said:

    What sort of critical interfaces are we talking here? Dealing with the web — well, any network protocol — means a lot of dealing with strings.

    Haskell has a thing called a newtype which lets you give a type a new distinct identity in the type system. This makes it trivially easy to make types that differ in "semantic meaning" but have the same inner representation. So, for example, you could never mismatch:

    newtype Name = Name Text
    newtype Description = Description Text
    

    unless you deconstructed the values explicitly.

    The absolute most egregious example I have found is in the authentication system. It has values with the type declaration:

    data Creds a = Creds Text [(Text, Text)]
    

    The first Text in there is supposed to be the authentication backend's name. The list of pairs has undefined semantics. The semantics are supposed to be defined by the authentication plugin. This is string-typing at its worst. If you write a plugin, you're supposed to compare the name field to your plugin's name, and only do inserts and lookups in the key-value-list if the names match. Now, imagine that a malicious plugin decides not to check...


  • Discourse touched me in a no-no place

    @Captain said:

    Now, imagine that a malicious plugin decides not to check...

    Take a few steps back from your rant and ask yourself this:
    Why are you installing malicious plugins?

    Seriously, why? (But auth is a WTF on all systems; there's a lot of different schemes, they work in wildly different ways, provide thoroughly different types of answers, etc.)



  • @dkf said:

    Why are you installing malicious plugins?

    Seriously, why?

    I get your point. And I'm not, as far as I know. But at some point, I have to stop reading code and start using it.

    @dkf said:

    (But auth is a WTF on all systems; there's a lot of different schemes, they work in wildly different ways, provide thoroughly different types of answers, etc.)

    Yeah, but the solution isn't to make the auth system uni-typed. It's to use distinct types for each type of auth backend, so bugs on your end are caught at compile time, before they can become security issues. Haskell has these things called type families... (I know I've said "Haskell has" a few times, but this really is relevant to my rant). A type family lets you define a type in the scope of a class instance. Creds could very sensibly be defined as:

    instance YesodAuth App where
      data Creds GoogleOAuth2 App = GoogleOAuth2Creds { ... fields ... }
    

    The framework uses these type families all over the place, so they clearly know about them. But they decided not to use them where it makes the most sense to. It makes me truly suspicious of the author's motives.

    So, stepping back, like you asked: I don't intend to. But at this point, I either assume that the whole framework is antagonistic (at best) and audit it all with a fine tooth comb, or move on to something else that is easier to audit.



  • @Captain said:

    in Haskell, there is a fairly common pattern for creating algorithms

    I thought this was Haskels true purpose?

    Now you're saying it's for web stuff?



  • Use stringified XML, problem solved.


Log in to reply