frege

What is the motivation behind the "let definition is a constant" hint?


With -hints option on, the compiler emits a hint message against the following program:

module Main where

main :: IO ()
main = do
    let magic :: Int  -- (A)
        magic = 123
    println magic

.

$ fregec -hints Main.fr
H Main.fr:5: let definition is a constant: magic
calling: javac -cp /home/yohashi/lib/java/fregec.jar:. -d . -sourcepath . -encoding UTF-8 ./Main.java 

What does this hint try to "hint" against?


If I omit the type annotation (sorry for incorrect terminology) on the line (A), the hint goes away:

main = do
    let magic = 123
    ...

Type ascription doesn't bring a hint, either:

main = do
    let magic = 123 :: Int

The same thing happens to where declarations:

main = println magic
  where
  magic :: Int
  magic = 123                     -- let definition is a constant: magic
  magica = 123                    -- no hint
  magicb = 123 :: Int             -- no hint
  magicfun :: Int -> Int
  magicfun = succ                 -- let definition is a constant: magicfun
  magicfuna = succ                -- no hint
  magicfunb = succ :: Int -> Int  -- no hint
  magicfunc :: Int -> Int
  magicfunc i = succ i            -- no hint

A hint on magicfun is particularly annoying because it discourages the point-free notation (compare to magicfunc).

So my question is: what is the motivation behind this hint? I think giving an alias for expressions simple or complex is a valid use for let/where. Is the hint suggesting otherwise?


Solution

  • You are right that abbreviating a constant or aliasing a function is perfectly fine. But then, hints are not warnings, but merely comments about your program that may or may not tell you something you didn't know yet.

    Hence the notion of "hint against" is mistaken. Nor should you strive to make your code "hints free".

    Ironically, it looks like the hint in question would need another hint to explain it. It should read:

    I, the compiler, use to move type annotated constants like your 'name' to the top level, because it is safe to do so and may eliminate some or all nested levels of let. This will also speed up things later in type checking, code generation and run time. In addition, you might consider to do the same yourself, lest you end up defining the same constant over and over in different let expressions or where clauses.

    Please note that, in

    foo :: Int
    foo = 42
    

    we are annotating foo, while in

    foo = 42 :: Int
    

    we are annotating the right hand side only, so that technically, foo is not annotated and must undergo type inference. So that's where the seemingly unjustified and confusing difference comes from.