4 ms·
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 (?) o
by md224 7y ago
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.
- BoiledCabbage 7y agoIn 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.
- TheAsprngHacker 7y agoThe 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.
- taneq 7y agoStill 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.
- avmich 7y agoI 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?..
- mrkeen 7y agoAbstraction 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.
- Twisol 7y agoThe 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.
- Chris_Newton 7y agoDoesn'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%93Howard_isomorphism https://en.wikibooks.org/wiki/Haskell/The_Curry%E2%80%93Howa...