There are several guidelines for creating memoization (look at here for a recent discussion).
First use the O2 optimization flag with the GHC compiler. Secondly, use a monomorphic type signature. What is your intermediate list (s) that you want to reach?
Then pay attention to your nested definitions. If the nested definition depends on the value of the argument in its covering ("external") area, this means that each call to this external function will have to create all its nested definitions again, therefore there will not be a single list that will be shared, but many separate independent.
Here, in your function, separating and naming the list expression that you want to use together, we get
memopascals rc = xs !! (r-1) !! (c-1) where xs = [[pascals ab | b<-[1..c]] | a<-[1..r]] pascals 1 _ = 1 pascals _ 1 = 1 pascals xy = memopascals (x-1) y + memopascals x (y-1)
Your definition of xs depends on the values of r and c , but you call your "external" memopascals function from within the nested, pascals . Each memopascals call must create its own copy of xs because it depends on the arguments of memopascals , r and c . Without the possibility of exchange.
If you need to have this dependent definition, you should settle down not to call it “out of scope”, but rather stay inside that area so that all internal (“nested”) definitions can be reused.
memopascals rc = frc where frc = xs !! (r-1) !! (c-1) xs = [[pascals ab | b<-[1..c]] | a<-[1..r]] pascals 1 _ = 1 pascals _ 1 = 1 pascals xy = f (x-1) y + fx (y-1)
Now each call to memopascals creates its own internal definitions (from its nested scope), which then call each other without ever calling from the scope - therefore, the xs list is reused, reaching the exchange.
But another call to memopascals will create its own new copy of the xs list internal definition and use it. We can call this "local" sharing, rather than "global" sharing (i.e., Memonymization). This means that it works quickly, but the second call with the same parameters takes the same time as the first, and not 0 times, as with a full record.
There is only one way to achieve this, and that should make your xs definition independent. Then the compiler can break all the nested frames of the frame, perform a lambda lift , turning the nested shutters into simple lambdas, etc. Etc.:
memopascals :: Int -> Int -> Integer memopascals rc = [[pascals ab | b<-[1..]] | a<-[1..]] !! (r-1) !! (c-1) where pascals 1 _ = 1 pascals _ 1 = 1 pascals xy = memopascals (x-1) y + memopascals x (y-1)
With the -O2 switch, the GHC performs full memoization even for this version. Until we forget the signature of the monomorphic type (or its local exchange again).