Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Without types, it is difficult to compose pipelines together.

I would gladly hear this argument expanded. It's really not obvious to me that that's the case.



Suppose I give you two functions f, and g. Can you run f(g()) without breaking things? The honest answer is you don't know until you read the functions, which is a slow and difficult thing to do.

Suppose I give you functions f and g of respective types int -> str and Nothing -> str. Can you compose them? No, and you see this immediately from the types. Types make reasoning about composability a lot easier.

Of course, it's not a panacea, and it's less helpful the more side effects a function has. Can we compose pure int->int functions? Of course! Can we compose two of them where the second expects some image to exist in some docker registry? You'll need to read the first to be able to tell.

Given the highly side effectful nature of pipelines, I'd think the applicability of types would be limited. But maybe that's just a lack of imagination on my part.

Certainly information like "this pipeline expects these variables" and "this pipeline sets these variables" are susceptible to a typed approach, and it would make things easier. By how much, I don't know.


What are side-effects but undocumented arguments and returns?

Firstly, you want to ensure your functions are pure with respect to input. That is to say, they might reference a configuration or context object that is passed to them as an argument, but they'll never reference some global object/variable.

So then the docker image inside some docker registry? Both the image and the registry are values in the config/context argument at the least. Maybe they're their own separate arguments depending on whether you prefer a single big object argument or a bunch of smaller more primitive arguments.

So then the pure function that expects the docker image to exist in some registry is no longer

  Int -> Int
It's now

  String -> String -> Int -> Int
because it needs a registry and an image. Maybe it's

  String -> String -> String -> String -> Int -> Int
because there's a username and password required to access the registry. Icky, but if we make a few types like

  data Registry { 
    user :: String,
    password :: String,
    url :: String
  }
that becomes

  Registry -> String -> Int
But we could make it better by doing something like

  data Foo { 
    reg:: Registry, 
    image :: String
  }
and now the function can be

  Foo -> Int -> Int
This doesn't fix the image not actually existing in the registry, but at least now we know that the two functions aren't composable, and when it fails because the image doesn't exist we can hopefully trace through to see who the caller is that's giving it incorrect data.

PS: sorry if i got the haskell typing wrong. I don't know haskell so that's the result of what i could cobble together from googling about haskell type syntax




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: