Type of list ranges: how can I make my function less polymorphic?
-
18-06-2021 - |
题
I have a function
sasiad (x,y) = [ (x+dx,y+dy) | dy <- [-1..1], dx <- [-1..1], x+dx >= 0, y+dy >= 0]
I don't like the type of that function. I would like it to return [(Int,Int)]
instead of [(t,t1)]
Is it possible to force ghci to make that?
解决方案
Yes, add a type annotation:
sasiad (x,y) = [ (x+dx,y+dy) | dy <- [-1..1], dx <- [-1..1], x+dx >= 0, y+dy >= 0] :: [(Int, Int)]
其他提示
You can add a type annotation as dave4420 mentions, but the normal way of doing so is this:
sasiad :: (Int, Int) -> [(Int, Int)]
sasiad (x,y) = [ (x+dx,y+dy) | dy <- [-1..1], dx <- [-1..1], x+dx >= 0, y+dy >= 0]
There is, however, an argument to be made for using the type that the compiler infers:
sasiad :: (Ord t1, Ord t, Num t1, Num t, Enum t, Enum t1) => (t, t1) -> [(t, t1)]
As this blog entry argues, the more complex type has advantages. For example, the fact that the inferred type for your function distinguishes between t
and t1
means that if you declare this type, the compiler won't let you mix up the arguments; basically, this type guarantees that the first elements of the pairs in the result list are computed using x
only, and the second elements are computed using y
only. Whether this is an useful invariant depends on your program.
Also, I can't help but to refactor your function:
sasiad :: (Ord t1, Ord t, Num t1, Num t, Enum t, Enum t1) => (t, t1) -> [(t, t1)]
sasiad (x,y) = cross (generate x) (generate y)
where generate x = filter (>=0) . map (\dx -> x+dx) $ [-1..1]
cross xs ys = [ (x,y) | x <- xs, y <- ys ]