How to make a CAF not a CAF in Haskell?

前端 未结 7 1013
南旧
南旧 2020-12-01 05:23

How do I make a Constant Applicative Form into, well, not a Constant Applicative Form, to stop it being retained for the lifetime of the program?

I\'ve tried this ap

相关标签:
7条回答
  • 2020-12-01 06:13

    Whenever you use () as a parameter, what you are going to say is actually

    Although I have declared a parameter here, I am not interesting in what it is and I am not going to do anything with its value.

    You are not interesting in it, because () does not have anything interesting at all; you are not going to do anything with it, because you can do nothing with ().

    The problem of it is that the compiler have the right to optimise it out since there is only one possible value to pass so its use is always predictable and so why not assume it? But it moves it back to CAF and makes the idea does not work.

    Fortunately, there is another way to do so. Look at the following modification of twoTrues:

    twoTrues :: a -> [[[Bool]]]
    twoTrues _ = map (++ (True : repeat False)) . trueBlock <$> [1..]
    

    Now you can use twoTrues like this:

    map concat $ twoTrues()
    

    Since a is an unused type parameter, the caller can pass anything. And because you don't know what it would be so you have no idea what you can do with it. This is actually forcing you to ignore its value. So it is basically declaring the same statement I mentioned before.

    Of cause, you can now pass any thing (including undefined) to that function. But it does not matter and actually it is this possibility makes this trick workable, since the compiler can no longer predict how this function being used. When the human user sees this function they should know what you are going to say here and conclude passing () is the easiest, but even if they don't and passing something else, it would not break anything and since Haskell is lazy the additional parameter could never be evaluated at all.

    So what if () being used as a result? This is even worse. Since returning () means your function do not do anything at all (in Haskell, all effects of a function shall represented in its return value), the compiler have the right to conclude your function is not necessary.

    The conclusion is, () as a type shall not appear in type signatures unless used with some other type (i.e. in IO ()).

    EDIT

    Now one may wonder, if there is only one way to implement a -> String from a String, why the compiler cannot conclude they are the same. The answer turn out to be that you actually have two way to implement this.

    usual :: a -> String
    usual _ = "Hello World!"
    
    unusual :: a -> String
    unusual a = seq a "Hello World!"
    

    For almost all input, usual value = unusual value, but usual undefined is "Hello World!" whilst unusual undefined is undefined.

    In a human point of view, unusual is pretty unusual, because it forces to evaluate a value unrelated to the final result. If in any case you do need such a thing, simply call seq would be easier. Furthermore, since Haskell is by default lazy, if you want to define a function that is strict, you'd better document this behavior. So if you see such a signature without additional documentation you have the right to assume it is implemented in the usual way.

    0 讨论(0)
提交回复
热议问题