News for hackers. Stuff that matters.
Re:Implement with Types, Not Your Brain by quickthrower2 on Tuesday June 25, 2019 @07:06PM (#20280133)
> I don't need to use my brain to do programming

...

    hoistStateIntoStateT
        :: Sem (State s ': r) a
        -> S.StateT s (Sem r) a
    hoistStateIntoStateT (Sem m) = m $ \u ->
    case decomp u of
        Left x -> S.StateT $ \s ->
        liftSem . fmap swap
                . weave (s, ())
                        (\(s', m') -> fmap swap
                                    $ S.runStateT m' s')
                        (Just . snd)
                $ hoist hoistStateIntoStateT x
        Right (Yo Get z _ y _)     -> fmap (y . (<$ z)) $ S.get
        Right (Yo (Put s) z _ y _) -> fmap (y . (<$ z)) $ S.put s

...

I definitely need to use my brain to read that code though.

Re:Implement with Types, Not Your Brain by munchbunny on Tuesday June 25, 2019 @07:10PM (#20280175)
I have a similar experience with mathematics. "The differential form expressions for Maxwell's equations are incredibly simple!" Yup, they're simple after you've done all of the work to wrap your mind around differential forms in the first place. I did it at some point. It was rewarding, and it was elegant, and it was even fun, but it was anything but easy on my brain.

Haskell feels a lot like that. That code snippet gave me the same feeling of having to climb a conceptual mountain to appreciate the metaphorical view at the top. I have no doubt that there is something beautiful there, but of all of the conceptual, programming related mountains I need to climb, the language abstraction one is pretty low priority, because the language I am using really is not my limiting factor. I appreciate that others who care spend a lot of time thinking about this problem, but Haskell's got a long way to go before I'm convinced that it's worth using in everyday contexts.

Re:Implement with Types, Not Your Brain by schwurb on Tuesday June 25, 2019 @07:50PM (#20280492)
Note that Haskell makes very few assumptions about the users; the quality and complexity of Haskell code varies greatly. The author of this block is dealing with an abstract topic, resulting in somewhat arcane looking code (I am sure that after some refactoring it would read nicer). It is however very possible to write really easy to understand Haskell code by ignoring its more fancy features and sticking to the most used abstractions: Monads, Applicatives and Functors. They alone are enough to write great code, no category theory, effect library, type magic or whatever is needed. Simple, real-world Haskell exists, it is just not often blogged about.
Re:Implement with Types, Not Your Brain by munchbunny on Tuesday June 25, 2019 @07:59PM (#20280555)
Fair enough, I just disagree with the premise that heavily relying on the type system actually lets you think less hard about how to implement your code. In my experience, defining the types you'll use at module boundaries is the actual hardest part. The stuff in the middle is usually straightforward until you realize you need sub-modules, at which point you're defining module boundary types again.
Re:Implement with Types, Not Your Brain by seanmcdirmid on Tuesday June 25, 2019 @11:06PM (#20281533)
A good type is like a heads up display about where you are in your implementation. Haskell types are often far away from being that, however.
Re:Implement with Types, Not Your Brain by quickthrower2 on Tuesday June 25, 2019 @07:16PM (#20280231)
Absolutely, and I have put a decent amount of time in to learning Haskell. But I've been imperative programming since 8 yo, and as much as I love Haskell I can't be saved from finding it hard to understand. I also find it unmemorable - I could get my head around that code if I tried but 2 weeks later I will forget.

I also find that code quite abstract, it looks to be solving some kind of problem that usually the language designer (if say you are in C#) would be solving. Haskell seems to be something that leaks it's mechanics into your code, where as C# keeps it for the most part nicely separated from your code, albeit with the effect of giving the programmer less power. No typeclasses etc.

Re:Implement with Types, Not Your Brain by sameerds on Wednesday June 26, 2019 @08:54AM (#20284432)
That criticism looks unfair to me because you did not quote the very next sentence which was relevant:

  "Gee, that’s complicated! I must be really smart to have
   written such a function, right?

   Wrong! I just have a trick!"
Seems like the author deliberately picked an example that looks hard but isn't once you understand what he wants to say.
Re:Implement with Types, Not Your Brain by stcredzero on Tuesday June 25, 2019 @07:56PM (#20280543)
I definitely need to use my brain to read that code though.

Nothing new under the sun. How about APL?

https://medium.com/@gordonguthrie/the-beam-needs-an-apl-y-la...

A professor of mine used to write commercial software in APL. (Including business apps!)

Re:Implement with Types, Not Your Brain by mamcx on Tuesday June 25, 2019 @11:05PM (#20281530)
I think APL fit well for business apps. Now, if you tell me some lunatic use it for make AAA games...
Re:Implement with Types, Not Your Brain by nine_k on Tuesday June 25, 2019 @07:35PM (#20280381)
I take a new unfamiliar thing and I have no idea how to work with it.

I take an old familiar tool and I instantly remember how to use it.

What gives?

When I was a boy, wise practitioners told me that being a programmer is a path of constant learning. They were right. And since then, I take learning as a natural part of the profession. I suspect it also has something to do with a high salary it commands.

Re:Implement with Types, Not Your Brain by munchbunny on Tuesday June 25, 2019 @07:51PM (#20280506)
Personally, the issue isn't learning vs. not learning. The issue is where to put the energy and time that I do have for learning. For example, in my current job, the top four items in my list are all about conceptual mastery in a specific domain (cybersecurity) so that I can design better systems. For programming languages, the usual suspects are good enough. The real limitation is the intersection of human behavior interacting with a cripplingly complex technical system, and you can make multiple careers studying just that.

Somewhere down closer to #15 is "learn Haskell," which comes after learning Rust/Go. Learning a "real" functional language would be much higher, but I already did that early in my career.

Re:Implement with Types, Not Your Brain by nine_k on Tuesday June 25, 2019 @08:46PM (#20280857)
Indeed, one does not always need to learn new tools when available tools suffice; the learning energy can be more effectively spent elsewhere.

I only meant to notice that there's no impenetrable barrier to mastery of that, when desired. I don't think that to learn Haskell at a high level is harder than to do the same with C++. The latter just had more adoption for last 25 years, for a number of reasons.

In any case, some (cursory) knowledge of Haskell improved my Java, Python, and JavaScript quite a bit, and is directly applicable to Scala in many aspects.

Re:Implement with Types, Not Your Brain by EdwardDiego on Tuesday June 25, 2019 @09:27PM (#20281082)
> Programs must be written for people to read

- Harold Abelson

Looking at that code, it looks like it's working on a tree of some sort, but what kind? Something to do with Sem and State and Yo, I guess. At a wild guess, it's something to do with ASTs and DSLs or similar. But code that is dramatically inscrutable without metric shit tonnes of context, is bad code.

Re:Implement with Types, Not Your Brain by tathougies on Tuesday June 25, 2019 @07:15PM (#20280221)
No you dont. Thats the point. The type of the function completely determines the operation. Dont read the code. Read the type and then you know what the code has to do.
Re:Implement with Types, Not Your Brain by md224 on Tuesday June 25, 2019 @07:38PM (#20280401)
> The type of the function completely determines the operation.

Really? I must be misunderstanding what you mean by "operation", or maybe this is the part of functional programming that hasn't clicked for me yet... my understanding is that the type signature tells you what kinds of things the function works with, but it doesn't tell you how the function maps the input to the output. When I hear "operation", I think of the latter. What are you referring to?

Re:Implement with Types, Not Your Brain by derefr on Tuesday June 25, 2019 @07:45PM (#20280448)
Sometimes there's only one possible function of a given typing.

For example, if a function's type is `a -> b -> (a, b)`, there's really only one function fitting that shape: cons. What else could a (pure) function possibly do, with that typing, other than "exactly what cons does"?

Re:Implement with Types, Not Your Brain by etbebl on Tuesday June 25, 2019 @08:19PM (#20280712)
Map x : a and y : b to (5x, y-3)?
Re:Implement with Types, Not Your Brain by chas on Tuesday June 25, 2019 @08:44PM (#20280848)
In a Haskell context, that function’s type would be something like ‘(Num x, Num y) => a -> b -> (a, b)’ where ‘Num’[0] is the type class of types with defined numerical operators. If there is no fat arrow (=>), you can only perform operations which don’t depending on any aspects of the types in question.

There is, however, a gotcha along these lines: Haskell has a value called ‘undefined’ (along with some other issues collectively called “bottom” for CS theory reasons[1]) which can take any type. So ‘foo x y = (x, undefined)’ is a legal implementation of that function will will compile, but crash if you try to do anything with the ‘undefined’ result.

In practice, knowing your function has essentially one implementation because it’s sufficiently polymorphic (no concrete types), is still a great trick for getting the compiler to enforce certain kinds of correctness.

[0] https://www.haskell.org/tutorial/numbers.html

[1] https://andre.tips/wmh/brief-note-undefined/

Re:Implement with Types, Not Your Brain by Jtsummers on Tuesday June 25, 2019 @08:28PM (#20280767)
Then you’ve forced the types a and b to be numerics or integers and the function type would be: Num -> Num -> (Num, Num) or similar and not the original type.
Re:Implement with Types, Not Your Brain by md224 on Tuesday June 25, 2019 @08:56PM (#20280911)
I'm not the person you're replying to, but my guess is that their example was an attempt to raise the same question that I have: how do we know the values (?) of the a-type and b-type variables weren't modified within the function in some way before being outputted as (a, b)? Doesn't the type signature leave an infinite amount of ambiguity as to the actual operation of the function, even if it specifies the types of the input and output? Variables of type "a" and "b" could have (I assume) many different values (just as a variable of type Num could be 1 or 1000), so how do you know the function doesn't change their values? Or is there a strict relationship between type and value that I'm missing here?

Do functional programmers consider the type of a variable to be more important (in some sense) than its value? If so, that could explain my confusion here, and that would be a pretty important thing to understand.

Edit: This idea -- "the type of the function completely determines the operation" -- seemed counter-intuitive to me at first, but I think that's actually a sign that it's a key concept I need to understand to really grok functional programming in a deeper way. (I think this is the case for counter-intuitiveness more generally... it's a sign that you've found the missing link in your understanding of something.) Thanks for the replies, this has been helpful.

Re:Implement with Types, Not Your Brain by BoiledCabbage on Tuesday June 25, 2019 @09:06PM (#20280968)
In that specific case, how do you modify it? What function can you use. You dont know that a & b are numeric, so you can use arithmetic, you dont know that their strings, you actually have put zero constraints on the types of a and b, so there are almost no sufficiently generic functions you could apply to them. You could pass them to the "identity" function to "modify" them, but that's not really changing them. Just about the only thing you can do to match the types is construct a tuple.

A (somewhat faulty analogy) is imagine a method that takes in two variable odds type Object and returns a tuple of Objects. You can call any instance method on the object class, and any static method, but can't reference any class variables. And you can do any casts to or from Object. There is almost nothing left you can do but return a tuple of them. The types constrained your implementation.

Re:Implement with Types, Not Your Brain by TheAsprngHacker on Tuesday June 25, 2019 @09:06PM (#20280969)
The submission mentions "theorems for free," which answers your question. The a and b type variables can be any type. Because the code has to work for anything, there are not too many operations that you can do on the values of types a or b. For example, if x :: a, you can't do x - 1 because that would require the constraint Num a.
Re:Implement with Types, Not Your Brain by taneq on Tuesday June 25, 2019 @10:16PM (#20281304)
Still seems to me (and I feel that the volume of discussion on this point confirms it) that this approach has a higher cognitive load than doing things slightly less abstractly and slightly more explicitly.
Re:Implement with Types, Not Your Brain by avmich on Wednesday June 26, 2019 @12:22AM (#20281896)
I guess the volume of discussion can be attributable to that there are enough Haskellers and enough non-Haskellers.

Haskellers read some texts and got used to the idea of type manipulations. They know, for example, there is a type "Void" which doesn't contain any values - that is, by definition no object has this type - and therefore any function which has this type of argument can't be invoked. Because to invoke the function, it has to be passed a value of the given type, and there aren't any.

Haskellers got used to the idea that if we have function from type a to the same type a, and there are absolutely no restriction on what the type a is, then it should be identity function - they just sat on this idea, pondered it for some time and got to this conclusion. For non-Haskellers it's not clear - and even the idea that types can restrict code so tightly as to make code unique (up to isomorphisms) can be novel to them.

Does it make a bigger cognitive load? Probably not for those who got used to type properties like this. Is it hard for non-Haskellers to see this feature and try to use it? Well, many Haskellers passed through this stage, so it ought to be doable. Most importantly, can they agree on this?..

Re:Implement with Types, Not Your Brain by mrkeen on Wednesday June 26, 2019 @05:04AM (#20283130)
Abstraction is about eliminating the details, not ignoring them.

If I have ((a,b)->b), it behaves precisely the same way regardless of what those variables represent.

Do I need to do null check? Irrelevant. B is being reused, do I do a deep or a shallow copy? Does it have a copy constructor? Irrelevant.

Re:Implement with Types, Not Your Brain by Twisol on Tuesday June 25, 2019 @09:08PM (#20280982)
The function definition must work unchanged for any choice of types. In order to “change” their values, it would have to know what other values are possible and how to get them.

Frame it as a game: give me a function definition and I will give you a pair of types for which that function is statically ill-defined. You must give me a function that works, without changing how it’s defined, for whatever pair of types I tell you.

Re:Implement with Types, Not Your Brain by Chris_Newton on Tuesday June 25, 2019 @10:32PM (#20281372)
Doesn't the type signature leave an infinite amount of ambiguity as to the actual operation of the function, even if it specifies the types of the input and output?

Counter-intuitively, it’s actually because of ambiguity that you don’t have as much choice in how your function is implemented. If you know nothing about your types a and b, there is nothing you can do with values of those types to create new values (excluding oddities with “undefined” and the like, which I’ll ignore for the rest of this comment). The only things you can do are “structural” operations that use only some or all of the inputs you’ve been given, verbatim.

For example, given one value each of types a and b, you could have a function with a return type of a that just chose that input value and gave it back, but what other function of type a -> b -> a could you possibly have?

Now, extend that idea. Suppose you have a function of type a -> b -> (a, a). This needs to return a pair of a values, but we still only have one such value that we know. We don’t know how to make another a from an a, because we don’t know anything specific about that type. Similarly, we don’t know how to make an a from a b, or from an a and a b for that matter. So the only possible implementation of this function (with the caveat above) would be the one that returns a pair where each element is the a it was given as input.

Let’s extend that idea again. This time, suppose we have input values of types a and b, but we also have an input function of type b -> c. That is, we start with two values of ambiguous types, but we do know how to turn one of those values into a third type. In this case, we could write a function of type a -> b -> (b -> c) -> (a, c), for example, because although we don’t have an input value of type c directly, we do have a b and we know how to make a c from it. But, we have exactly one way we know to do that, so there is still only one possible implementation of a function of this type: we must take the a we started with, and we must take the b we started with and convert it to a c using the b -> c function we started with, and then we return a pair with the two final values.

When can we have more than one possible implementation? Essentially, when we have been given more than one way to make at least one of the required output values. For example, suppose we have a function of type a -> b -> (a -> c) -> (b -> c) -> (c, c). This time, we start with an a and a b, but we also know how to convert either of them to a c. We need two c values in our output, but nothing here says they have to be the same, so each of the output values could come from either of the a and b inputs, giving four possible implementations (but only four).

As a final example, as surprising at it might seem at first sight, there is no possible implementation at all of a function of type a -> b (again, excluding funny games with “undefined” and the like). We simply don’t have any way to make a b without knowing anything about the type itself and without being supplied with either a b value directly or some way to make one.

If you found that interesting and really want to blow you mind, you might enjoy the Curry-Howard isomorphism. The Wikipedia page isn’t a good introduction if you’re trying to understand it, IMHO, but Wikibooks has a nice introduction:

https://en.wikibooks.org/wiki/Haskell/The_Curry%E2%80%93Howa...

Re:Implement with Types, Not Your Brain by mcphage on Tuesday June 25, 2019 @07:51PM (#20280509)
Do you frequently find yourself having to redefine cons when programming?
Re:Implement with Types, Not Your Brain by posco on Tuesday June 25, 2019 @08:17PM (#20280692)
No, but surprisingly frequently this property holds: if your function is generic enough, there is only one (or a small number) of possible implementations that type check.

Leveraging this rules out bugs.

Re:Implement with Types, Not Your Brain by danharaj on Tuesday June 25, 2019 @08:46PM (#20280861)
If you define a custom generic data type, for example if you need a special kind of tree for a particular algorithm, then you're going to be writing a lot of generic functions like cons for that data type.
Re:Implement with Types, Not Your Brain by EdwardDiego on Tuesday June 25, 2019 @11:56PM (#20281770)
In what language? I thought Haskell and OCaml for example, had generics.
Re:Implement with Types, Not Your Brain by mrkeen on Wednesday June 26, 2019 @04:55AM (#20283088)
The situation is a bit better than just generics (as they are in Java). For instance, if I had a List<Int>, I can treat it like a List<I>, but why can't I treat it like an <L>Int? Or an <L><I>?
Re:Implement with Types, Not Your Brain by quickthrower2 on Tuesday June 25, 2019 @07:17PM (#20280238)

    hoistStateIntoStateT
        :: Sem (State s ': r) a
        -> S.StateT s (Sem r) a
?

Sorry even that'll get my brain CPU on 100% for a few minutes.

Re:Implement with Types, Not Your Brain by lalaithion on Tuesday June 25, 2019 @07:45PM (#20280450)
This code takes a Sem monad with a State effect in it, and turns it into a StateT monad with an inner monad of Sem and al l the other effects.

I have never user Polysemy, any effects library, or type-level lists before, but I'm reasonably sure my guess is right, despite not understanding any of the code.

Re:Implement with Types, Not Your Brain by nilkn on Tuesday June 25, 2019 @08:47PM (#20280866)
I suspect you probably just need more experience with the base language. And I don't mean that in a bad way -- Haskell is a bit of a weird language, and it's something that you'll need to spend some time with. But indeed that's a fairly standard sort of type signature that you'll process at a glance if you've used the language a fair bit.
Re:Implement with Types, Not Your Brain by tathougies on Tuesday June 25, 2019 @08:09PM (#20280632)
Takes a Sem with a state effect and turns it into the state monad transformer over Sem with the remaining effects?
Re:Implement with Types, Not Your Brain by pxtail on Wednesday June 26, 2019 @06:35AM (#20283616)
My god, this is horrible, it looks more like an ascii art than usable piece of code. Personally I don't even try to fiddle around with languages or libraries which are trying to get too fancy or too "clever" with their syntax.

The reason is simple: as a developer looking at the code written a week ago by myself, using "normal" language, I'm able to somewhat recognize what's going on, after a month or two it's something produced by aliens. With code depicted above this time will shorten to what? hours? days? Additionally it will require additional effort for developer himself or anyone else to decrypt it after a while.