3

I am a Haskell beginner, and when I compile the Haskell source code, when trying to make a function to implement an lazy at least as long as

module Main where

import Data.Maybe

main = undefined

foldWhilel :: (a -> b -> Maybe b) -> b -> [a] -> Maybe b
foldWhilel f acc xs = go acc xs
  where
    go :: (a -> b -> Maybe b) -> b -> [a] -> Maybe b
    go acc [] = Just acc
    go acc (x:xs) = if isNothing new_acc then Just acc else go (fromJust new_acc) xs
      where
        new_acc :: Maybe b
        new_acc = f x acc

with this error pesky stupid nonsense error, considering (at least for what I know) that this should perfectly work. Because the function go and the function parameter are well designed in the type annotations

dist-newstyle/gh.hs:15:19: error:
    • Couldn't match type ‘b2’ with ‘b’
      ‘b2’ is a rigid type variable bound by
        the type signature for:
          new_acc :: forall b2. Maybe b2
        at dist-newstyle/gh.hs:14:9-26
      ‘b’ is a rigid type variable bound by
        the type signature for:
          foldWhilel :: forall a b.
                        (a -> b -> Maybe b) -> b -> [a] -> Maybe b
        at dist-newstyle/gh.hs:7:1-56
      Expected type: Maybe b2
        Actual type: Maybe b
    • In the expression: f x acc
      In an equation for ‘new_acc’: new_acc = f x acc
      In an equation for ‘go’:
          go acc (x : xs)
            = if isNothing new_acc then Just acc else go (fromJust new_acc) xs
            where
                new_acc :: Maybe b
                new_acc = f x acc
    • Relevant bindings include
        new_acc :: Maybe b2 (bound at dist-newstyle/gh.hs:15:9)
        f :: a -> b -> Maybe b (bound at dist-newstyle/gh.hs:8:12)
        foldWhilel :: (a -> b -> Maybe b) -> b -> [a] -> Maybe b
          (bound at dist-newstyle/gh.hs:8:1)
   |
15 |         new_acc = f x acc
   |                   ^^^^^^^

dist-newstyle/gh.hs:15:21: error:
    • Couldn't match expected type ‘a’ with actual type ‘a1’
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          go :: forall b1 a1. b1 -> [a1] -> Maybe b1
        at dist-newstyle/gh.hs:10:5-29
      ‘a’ is a rigid type variable bound by
        the type signature for:
          foldWhilel :: forall a b.
                        (a -> b -> Maybe b) -> b -> [a] -> Maybe b
        at dist-newstyle/gh.hs:7:1-56
    • In the first argument of ‘f’, namely ‘x’
      In the expression: f x acc
      In an equation for ‘new_acc’: new_acc = f x acc
    • Relevant bindings include
        xs :: [a1] (bound at dist-newstyle/gh.hs:12:15)
        x :: a1 (bound at dist-newstyle/gh.hs:12:13)
        go :: b1 -> [a1] -> Maybe b1 (bound at dist-newstyle/gh.hs:11:5)
        f :: a -> b -> Maybe b (bound at dist-newstyle/gh.hs:8:12)
        foldWhilel :: (a -> b -> Maybe b) -> b -> [a] -> Maybe b
          (bound at dist-newstyle/gh.hs:8:1)
   |
15 |         new_acc = f x acc
   |                     ^

dist-newstyle/gh.hs:15:23: error:
    • Couldn't match expected type ‘b’ with actual type ‘b1’
      ‘b1’ is a rigid type variable bound by
        the type signature for:
          go :: forall b1 a1. b1 -> [a1] -> Maybe b1
        at dist-newstyle/gh.hs:10:5-29
      ‘b’ is a rigid type variable bound by
        the type signature for:
          foldWhilel :: forall a b.
                        (a -> b -> Maybe b) -> b -> [a] -> Maybe b
        at dist-newstyle/gh.hs:7:1-56
    • In the second argument of ‘f’, namely ‘acc’
      In the expression: f x acc
      In an equation for ‘new_acc’: new_acc = f x acc
    • Relevant bindings include
        acc :: b1 (bound at dist-newstyle/gh.hs:12:8)
        go :: b1 -> [a1] -> Maybe b1 (bound at dist-newstyle/gh.hs:11:5)
        f :: a -> b -> Maybe b (bound at dist-newstyle/gh.hs:8:12)
        foldWhilel :: (a -> b -> Maybe b) -> b -> [a] -> Maybe b
          (bound at dist-newstyle/gh.hs:8:1)
   |
15 |         new_acc = f x acc
   |           
6
  • 3
    You need to enable (and then use) ScopedTypeVariables Commented May 15, 2022 at 19:52
  • 4
    'Generics' in Haskell refers to datatype generic programming. Languages like Java use generics for what Haskell calls (parametric) polymorphism. Commented May 15, 2022 at 20:00
  • 4
    Minor note: using isNothing and fromJust is a common beginner anti-pattern. In many languages, one needs a boolean to perform a conditional like if myBoolean then .. else .., so it's easy to be tempted to use isNothing. However, that loses information, reducing the type Maybe a to Bool and the values Nothing / Just x to True / False -- note how x was lost! Then, fromJust can crash the program if we call it by mistake on Nothing. You don't have to risk crashes if you use pattern matching: case myMaybeVal of Nothing -> ... ; Just x -> ... use x is 100% safe. Commented May 15, 2022 at 20:26
  • @chi I think that Haskell should no have any if keyword, because Haskell elementary operation is pattern matching. Commented May 16, 2022 at 15:40
  • @chi Where I can find material about Haskell patterns and anti-patterns. Commented May 16, 2022 at 15:49

1 Answer 1

4

The simplest way to fix the error is to remove type signatures for go and new_acc.

You cannot use type signatures this way because say b used in the signature of foldWhilel and b used in the signature of new_acc are two completely different variables that just happen to have similar names. On top of that, (a -> b -> Maybe b) ingo signature is wrong regardless (go has no such parameter, is is using f from an outer scope).

If you want to use b throughout you function with the same meaning, you need to enable and apply Scoped type variables:

{-# Language ScopedTypeVariables #-}

import Data.Maybe

foldWhilel :: forall a b. (a -> b -> Maybe b) -> b -> [a] -> Maybe b
foldWhilel f acc xs = go acc xs
  where
    go :: b -> [a] -> Maybe b
    go acc [] = Just acc
    go acc (x:xs) = if isNothing new_acc then Just acc else go (fromJust new_acc) xs
      where
        new_acc :: Maybe b
        new_acc = f x acc

Demo

Sign up to request clarification or add additional context in comments.

5 Comments

Imho scoped type variables should be on by default. Every other language I can think of that has generics has them in-scope during the function body. It's just so much more intuitive.
It is also the only(?) extension where ghc won't suggest enabling it, so if you are not aware of it you have to figure it out by asking on SO
is the keyword forall by default or has to be enabled by the extension?
@Iceland_jack Indeed. We get a warning when we shadow a variable, we should also get one when we shadow a type variable, suggesting to either rename it or to turn on scoped type variables. (Or just make scoped variables the default)
@Delfin forall is a non-standard keyword enabled by this extension (and some others). You need to use it in order to make a variable scoped.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.