1

I'm trying to use seq in list comprehension to force the generated elements to be evaluated, but :sprint tells me that my list is actually not evaluated. For example:

Prelude> let foo a b = a + b
Prelude> let bar c = c + 1
Prelude> let lst = [bar $ foo x y | y <- [0..9], x <- [0..9]]
Prelude> :sprint lst
lst = _
Prelude> seq lst ()
()
Prelude> :sprint lst
lst = _

But other people's map seems to work properly:

Prelude> let xs = map (+1) [1..10] :: [Int]
Prelude> :sprint xs
xs = _
Prelude> seq xs ()
()
Prelude> :sprint xs
xs = _ : _

Why is my seq not working?

Chien
  • 355
  • 2
  • 11
  • you don't have that issue in a compiled *.hs file because when the compiler is done everything will have a concrete type – Random Dev Apr 29 '21 at 17:40
  • @Carsten Thank you, this does help me out! – Chien Apr 29 '21 at 17:42
  • `GHCi> :set +t` and it will print the types of each calculated value. so you will always see right away whether the type is polymorphic or it is monomorphic. – Will Ness Apr 30 '21 at 15:29

1 Answers1

3

you did not give lst a concrete type so for ghci it's a new instance/value every time you use it (and GHCi will choose a default type for it)

if you did not change anything you should even see a warning like this:

<interactive>:19:1: warning: [-Wtype-defaults]
    • Defaulting the following constraints to type ‘Integer’
        (Show a0) arising from a use of ‘print’ at <interactive>:19:1-10
        (Num a0) arising from a use of ‘it’ at <interactive>:19:1-10
        (Enum a0) arising from a use of ‘it’ at <interactive>:19:1-10
    • In a stmt of an interactive GHCi command: print it

Add the :: [Int] and it should work:

> let foo a b = a + b
> let bar c = c + 1
> let lst = [bar $ foo x y | y <- [0..9], x <- [0..9]] :: [Int]
> :sprint lst
lst = _
> seq lst ()
()
> :sprint lst
lst = _ : _

I like to think about generic types as having additional invisible parameters: the types for the type-parameters - so it's more a function then a value ;) (of course in reality it's not really exactly like this, but it helps me around issues like this)


EDIT as @dfeuer pointed out the comment about the parameters/invisible-functions might be misleading or even wrong.

Sadly I cannot claim to really know how GHCi deals with this (I'll gladly add something when I find a source) but so far my intuition would be, that GHCi does/cannot create a value till all dictionaries for the type-class constraints are known - so if there is no constraint it can and will create the value (at runtime there is no type so this is no problem).

In the above example the implicit Num constraint (0, 1, 9 and +) demands such an dictionary for the Num instance of the involved type.

Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • Yes it did! Thank you so much! I guess that means if i explicitly declare a variable to be `[Int]` in my .hs file then `seq` should also work properly on it? – Chien Apr 29 '21 at 17:40
  • as I commented above (sorry my bad) - in a compiled file you should never have that issue - when you finally *use* the value it has to have a *concrete* type and of course it should be a fixed value you are pointing to - it's really just how GHCi handles generic types – Random Dev Apr 29 '21 at 17:42
  • remark: of course if you define it just like this inside a .hs file and load it into GHCi you end up here again - but if you compile your file into a program and run it you won't - sorry if this was not clear – Random Dev Apr 29 '21 at 17:46
  • 1
    The talk of invisible parameters is just imprecise enough to give the wrong impression. `map Const "Hello!"` has type `[Const Char a]`, but it's still *operationally* just one value. – dfeuer Apr 29 '21 at 20:19
  • @dfeuer sorry about that - I'd hope it could help a bit. I know it's more complex then that but to be honest I only have some *intuition* about how GHCi handles that (my hunch: can you actually create the value- for example if you have a constraint you are back: `map Const "Hello!" :: Num a => [Const Char a]`) - Do you have somewhere I can read the actual semantics (I'm surely interested in improving my intuition) and more importantly: would you prefer me removing the comment? – Random Dev Apr 30 '21 at 04:16
  • @Carsten, it's only operationally a function if there are class constraints. – dfeuer Apr 30 '21 at 06:18
  • @dfeuer I'll gladly add this (fits my intuition - I (playing GHCi/whatever) can produce a value only if I have all *type-class* dictionaries needed) - I'm still interested in reading more about this - is there a source on how/when GHC(i) resolves those? I doubt it's in the report right? – Random Dev Apr 30 '21 at 06:47
  • 1
    @Carsten, definitely not in the report. Dictionaries are an implementation matter. I don't know where you can read about it, but you might find it interesting to look at one of Richard Eisenberg's presentations about the different kinds of arguments that can exist. Visible vs. inferred, relevant vs. irrelevant (extending to linearity), type vs. term. I don't have links off-hand, but I think there's at least one video on YouTube somewhere. – dfeuer Apr 30 '21 at 07:59
  • thanks - I'll look for it – Random Dev Apr 30 '21 at 10:00