类型变量 r 已超出其作用域

The type variable r has escaped its scope

我有一个拼图的类型同义词,定义如下:

type Cell = Int
type Board = Array Cell

type Puzzle = forall r.
  { board::Board
  , meta::
    { metaData :: {|r}
    , metaBoard :: (Array {|r})
    }
  }

我还包括了这样的想法,即策略采用拼图,returns 它包裹在三种状态之一中

type Strategy = Puzzle -> (Stateful Puzzle)

data Stateful a =
  Advancing a
  | Stable a
  | Finished a

最后,我有一个函数可以让我们(稍微)回到 Either 的工作方式。我的想法是,最终我可以开始编写策略,直到达到完成状态。

advanceOrFinish :: (Stateful Puzzle) -> (Stateful Puzzle)
advanceOrFinish (Advancing puzzle)
  | isSolvedOrInvalid puzzle.board = Finished puzzle
  | otherwise = Advancing puzzle
advanceOrFinish (Stable puzzle)
  | isSolvedOrInvalid puzzle.board = Finished puzzle
  | otherwise = Advancing puzzle
advanceOrFinish (Finished puzzle) = Finished puzzle

问题是我遇到了这个错误:

  The type variable r, bound at

    src/SC.purs:23:15 - 29:4 (line 23, column 15 - line 29, column 4)

  has escaped its scope, appearing in the type
                                              
    { board :: Array Int                      
    , meta :: { metaBoard :: Array (Record r7)
              , metaData :: Record r7         
              }                               
    }

没有Stateful(虽然没用)的相同功能没有这个问题。所以这很好:

advanceOrFinish :: Puzzle -> Puzzle
advanceOrFinish puzzle
  | isSolvedOrInvalid puzzle.board = puzzle
  | otherwise = puzzle

这个错误试图告诉我什么?

type Puzzle = forall r. ...并不代表你想的那样。

如果你有一个变量:

p :: Puzzle

然后您想对该变量做一些事情,例如:

b = p.board

在那一刻,当您按名称引用 p 时,您可以选择类型 r,然后变量 p 必须以某种方式“成为”那个类型,意思是 p.meta.metaData :: {|r}.

每次访问变量p时,都会发生这种情况。每次您选择某种类型 r(并且每次都可能是不同的类型),并且每次字段 p.meta.metaData 都必须是该类型。

很明显,这不能合理地工作。很明显,这不是你的意思。

forall 的意思是“我将为所有 类型工作 ”——这就是名字中的字面意思。消费者选择哪种类型,而不是实施者。


你的意思可能是要有一个带有类型参数的Puzzle,这样任何创建Puzzle实例的人都可以选择当时的类型,然后选择[=的实例24=] 将继续使用该类型。

这样做的语法是这样的:

type Puzzle r =
  { board::Board
  , meta::
    { metaData :: {|r}
    , metaBoard :: (Array {|r})
    }
  }

这里r称为Puzzle的“类型参数”。

然后,每当你提到Puzzle时,你必须说出r是什么,例如:

foo :: Puzzle (a :: Int) -> Int
foo p = p.meta.metaData.a

bar :: Puzzle (x :: String) -> Unit
bar _ = unit

如果您不关心它是什么,它也可以是通用的:

advanceOrFinish :: forall r. Puzzle r -> Puzzle r
advanceOrFinish puzzle
  | isSolvedOrInvalid puzzle.board = puzzle
  | otherwise = puzzle