首页 > 解决方案 > 类型变量 r 已超出其范围

问题描述

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

type Cell = Int
type Board = Array Cell

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

我还包含了这样一个想法,即一个策略需要一个 Puzzle 并将其返回为三种状态之一

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

这个错误想告诉我什么?

标签: purescript

解决方案


type Puzzle = forall r. ...并不意味着你认为它意味着什么。

如果你有一个变量:

p :: Puzzle

然后你想用那个变量做一些事情,比如:

b = p.board

在那一刻,当您p通过名称引用时,您可以选择一个 type r,然后该变量p必须以某种方式“变成”该类型,这意味着p.meta.metaData :: {|r}.

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

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

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


您可能的意思是Puzzle使用带有类型参数的 a,以便创建 的实例的人Puzzle可以在那一刻选择类型,然后 的实例Puzzle将继续使用该类型。

这样做的语法是这样的:

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

推荐阅读