1

我正在尝试使用依赖类型,但我陷入了一个似乎找不到原因的语法错误。这是我在具体语法中的第一个 lin 定义。

对于上下文,我一直在阅读 Inari 的嵌入式语法教程,并且我对制作可以从外部调用但实际上不会用于线性化的函数感兴趣,除非我从 Haskell 手动执行。如果您知道实现此目的的更好方法,请告诉我。

我的抽象语法:

cat
    S;
    Pre_S Clude ;
    VP Clude ;
    NP Clude ;
    V2 ;
    N ;
    Clude ; 

fun
    Include, Exclude : Clude ;

    To_S : Pre_S Include -> S ;

    Pred   : (c : Clude) -> NP c -> VP c -> Pre_S c ;
    Compl  : (c : Clude) -> V2 -> NP c -> VP c ;

    -- I'd like to call this one from Haskell
    Scramble : (c : Clude) -> VP c -> VP Exclude ;

    To_NP : (c : Clude) -> N -> NP c ;

    Bob, Billy, John, Mary, Lisa : N ;
    Liked, Loved, Hated : V2 ;

具体的:

lincat
    Pre_S, S, V2, NP, N = {s : Str} ;
    VP = {fst : Str ; snd : Str} ;

lin 
    To_S pre = {s = pre.s} ;
    Pred _ sub vp = {s = sub ++ vp.fst ++ vp.snd} ;
    Compl _ ver obj = {fst = ver ; snd = obj} ;

    Scramble _ vp = {fst = vp.snd ; snd = vp.fst } ;

    To_NP _ n = {s = n.s} ;

    Bob = {s = "Bob"} ; --and seven more like this

是因为这是不可能的,还是我做错了我无法找到的事情?

4

1 回答 1

1

1.修复语法错误

您的代码中的所有错误都不是由依赖类型引起的。第一个问题,实际的语法错误是这样的:

To_S pre = {s = pre.s} ;

pre是GF中的保留字,不能作为变量名使用。你可以写例如

To_S pr = {s = pr.s} ;

或更短——因为To_S看起来像一个强制函数,你可以这样做:

To_S pr = pr ;

修复后,您会收到新的错误消息:

     Happened in linearization of Pred
       type of sub
      expected: Str
      inferred: {s : Str}
      
     Happened in linearization of Compl
       type of ver
      expected: Str
      inferred: {s : Str}
      
     Happened in linearization of Compl
       type of obj
      expected: Str
      inferred: {s : Str}

这些固定如下。您不能将 a{s : Str}放入应该包含 Str 的字段中,因此您需要访问 sub .s、 ver .s和 obj .s

    Pred _ sub vp = {s = sub.s ++ vp.fst ++ vp.snd} ;
    Compl _ ver obj = {fst = ver.s ; snd = obj.s} ;

修复语法错误后的完整语法

这对我有用。

lin
    To_S pr = pr ;
    Pred _ sub vp = {s = sub.s ++ vp.fst ++ vp.snd} ;
    Compl _ ver obj = {fst = ver.s ; snd = obj.s} ;

    Scramble _ vp = {fst = vp.snd ; snd = vp.fst } ;

    To_NP _ n = {s = n.s} ;

    Bob = {s = "Bob"} ;
    Loved = {s = "loved"} ; -- just add rest of the lexicon

    Include, Exclude = {s = ""} ; -- these need to have a linearisation, otherwise nothing works!

2. 使用语法的提示和技巧

使用 Bob 和 Loved 的词典,它只生成一棵树。当我使用命令gt(generate_trees)时,如果我不给出类别标志,它会自动使用起始类别,即S。

> gt | l -treebank
To_S (Pred Include (To_NP Include Bob) (Compl Include Loved (To_NP Include Bob)))
Bob loved Bob

在 S 中解析也可以:

p "Bob loved Bob"
To_S (Pred Include (To_NP Include Bob) (Compl Include Loved (To_NP Include Bob)))

解析VP:添加一个linref

使用当前的语法,我们无法解析任何 VP:

> p -cat="VP ?" "Bob loved"
The parser failed at token 2: "loved"

> p -cat="VP ?" "loved Bob"
The parser failed at token 2: "Bob"

那是因为 VP 的 lincat 是不连续的并且没有单个s字段。但是,如果您还想解析 VP,您可以为 VP 的类别添加一个linref,如下所示:

  linref
    VP = \vp -> vp.fst ++ vp.snd ;

现在它甚至可以解析 VP,类似于:

Cl> p -cat="VP ?" "loved Bob"
Compl ?3 Loved (To_NP ?3 Bob)

这些奇怪的问号是元变量,我们接下来会修复它。

元变量

如果你读过我的博客,你可能已经看过关于元变量的一些内容:https ://inariksit.github.io/gf/2018/08/28/gf-gotchas.html#metavariables-or-those-question-解析时出现的标记

GF 有一个奇怪的要求,即每个参数都需要提供一个字符串(甚至是一个空字符串!),否则解析时无法识别。即使没有歧义,也会发生这种情况。

我们已经对空字符串进行了线性IncludeExclude——它们在任何情况下都必须进行一些线性化,否则整个语法都不起作用。所以我们需要将Clude' 的空字符串添加到所有的线性化中,否则 GF 解析器会被混淆。

在您刚刚Clude用下划线标记参数的所有线性化中,我们现在就这样做。不管我们将它添加到哪个字段,它只是一个空字符串,对输出没有任何影响。

    Pred _clu sub vp = {s = sub.s ++ vp.fst ++ vp.snd ++ _clu.s} ;
    Compl _clu ver obj = {fst = ver.s ; snd = obj.s ++ _clu.s} ;
    Scramble _clu vp = {fst = vp.snd ; snd = vp.fst ++ _clu.s} ;
    To_NP _clu n = {s = n.s ++ _clu.s} ;

在此之后,VP 中的解析工作没有问号:

Cl> p -cat="VP ?" "loved Bob"
Compl Exclude Loved (To_NP Exclude Bob)
Compl Include Loved (To_NP Include Bob)

Cl> p -cat="VP ?" "Bob loved"
Scramble Exclude (Compl Exclude Loved (To_NP Exclude Bob))

我还在获取元变量????

gt所以我们修复了它,但是当我得到一棵使用的树时,为什么我仍然会得到问号Scramble

> gt -cat="VP Exclude" 
Compl Exclude Loved (To_NP Exclude Bob)
Scramble ?3 (Compl ?3 Loved (To_NP ?3 Bob))
Scramble Exclude (Scramble ?4 (Compl ?4 Loved (To_NP ?4 Bob)))

> gt -cat="VP Include" 
Compl Include Loved (To_NP Include Bob)

那是因为该Scramble操作真正压制了它的论点。这不仅仅是 GF 编译器愚蠢并拒绝合作的情况,即使很明显存在哪个参数,在这种情况下,无法检索它是哪个参数:Scramble 将所有内容都变成VP Exclude.

添加 linref 并包含 Clude 的空字符串后的完整语法

只是为了完整起见,这里是所有的变化。

lin
    To_S pr = pr ;

    Pred _clu sub vp = {s = sub.s ++ vp.fst ++ vp.snd ++ _clu.s} ;
    Compl _clu ver obj = {fst = ver.s ; snd = obj.s ++ _clu.s} ;
    Scramble _clu vp = {fst = vp.snd ; snd = vp.fst ++ _clu.s} ;
    To_NP _clu n = {s = n.s ++ _clu.s} ;

    Bob = {s = "Bob"} ;
    Loved = {s = "loved"} ;

    Include, Exclude = {s = ""} ;

  linref
    VP = \vp -> vp.fst ++ vp.snd ;

3. 在没有依赖类型的情况下制作此语法的替代方法

如果您想使用 Python 中的语法或 C 运行时的 Haskell 绑定,那么您很不幸:C 运行时不支持依赖类型。因此,这是您的语法的一个版本,我们在其中使用具体语法和nonExist令牌中的参数来模仿行为(请参阅https://inariksit.github.io/gf/2018/08/28/gf-gotchas.html#raise-an -异常)。

我将文档保持在最小限度(因为这个答案已经很长了),但是如果您对此解决方案的某些部分有任何疑问,请问!

抽象语法

  cat
    S ; VP ; NP ; V2 ; N ;

  fun
    Pred   :  NP -> VP -> S ;
    Compl  :  V2 -> NP -> VP ;

    Scramble : VP -> VP ;

    To_NPIncl,
      ToNPExcl : N -> NP ;

    Bob, Billy, John, Mary, Lisa : N ;
    Liked, Loved, Hated : V2 ;

具体语法

  param
    Clude = Include | Exclude ;
  lincat
    -- To mimic your categories VP Clude and NP Clude,
    -- we add a parameter in VP and NP
    VP = {fst, snd : Str ; clude : Clude} ;
    NP = {s : Str ; clude : Clude} ;

    -- The rest are like in your grammar
    S, V2, N = {s : Str} ;

  lin

    -- No need for Pre_S, we can match NP's and VP's Clude in Pred
    -- Only make a sentence if both's Clude is Include
    Pred np vp = case <np.clude, vp.clude> of {
      <Include,Include> => {s = np.s ++ vp.fst ++ vp.snd} ;
      _ => {s = Predef.nonExist}
      } ;

    -- As per your grammar, V2 has no inherent Clude, but NP does
    Compl v2 np = {
      fst = v2.s ;
      snd = np.s ;
      clude = np.clude ;
      } ;

    -- Scramble doesn't look at it's argument VP's clude, 
    -- just makes it into Exclude automatically.
    Scramble vp = {
      fst = vp.snd ;
      snd = vp.fst ;
      clude = Exclude ;
      } ;

    -- Your grammar has the function To_NP : (c : Clude) -> N -> NP c ;
    -- We translate it into two functions.
    To_NPIncl n = n ** {clude = Include} ;
    To_NPExcl n = n ** {clude = Exclude} ;

    -- Finally, lexicon.
    Bob = {s = "Bob"} ;
    Loved = {s = "loved"} ;

现在,当我们生成 S 类中的所有树时,有一个使用 Scramble,但它没有线性化。

> gt | l -treebank
ClParam: Pred (To_NPIncl Bob) (Compl Loved (To_NPIncl Bob))
ClParamEng: Bob loved Bob
ClParam: Pred (To_NPIncl Bob) (Scramble (Compl Loved (To_NPIncl Bob)))
ClParamEng: 

也许比你的版本不那么优雅,甚至没有生成树,但这只是为了演示不同的方法。如果您正在使用 Haskell 并且不需要使用 C 运行时,请随时继续您的方法!

于 2021-06-20T01:12:04.350 回答