Search code examples
gf

Achieving restricted polymorphism from concrete file


Let's say I want to write a code that greets people, but for some reason, I don't want to greet a person called John, and I want GF to generate this kind of sentence.

Abstract Version

I knew that this kind of result is achievable from the abstract file such as follow: GreetingFromAbstract.gf

abstract GreetingFromAbstract = {

  flags startcat = Sentence;

  cat
    Relation;
    Sentence; 
    Verb Relation; 
    Noun Relation;
    


  fun
    MySentence : (rel : Relation) -> Verb rel -> Noun rel -> Sentence;
    Say : Verb RelA;
    Mike : Noun RelA;
    John : Noun RelB;
    RelA, RelB : Relation;
}

And the concrete file would be like this: GreetingFromAbstractEng.gf

concrete GreetingFromAbstractEng of GreetingFromAbstract = open SyntaxEng, ParadigmsEng, IrregEng, Prelude in {
  lincat
    Sentence = Phr; 
    Verb = V3; 
    Noun = N;
    Relation = {s : Str};


  lin
    MySentence _ action person = sayHi (action) (person);

    Say = mkV3(say_V) (noPrep) (to_Prep);
    Mike = mkN("Mike");
    John = mkN("John");
    RelA, RelB = {s = ""};


  oper
    -- Generate the final sentence
    sayHi : V3 -> N -> Phr =
      \v, n -> mkPhr(mkUtt(mkImp(mkVP
                                  (v)
                                  (mkNP (mkN "hi"))
                                  (mkNP (n)))));
   
  }

Puzzle

But let's say for some reason I don't want to do that from the abstract file but from the concrete. I want the person that is writing the names to decide which person to say hi to, and which is not.

Concrete Version

According to my problem, I wrote this code: GreetingFromConcrete.gf

abstract GreetingFromConcrete = {

  flags startcat = Sentence;

  cat
    Sentence; Verb; Noun;


  fun

    MySentence : Verb -> Noun -> Sentence;
    Say : Verb;
    Mike : Noun;
    John : Noun;
}

And the concrete: GreetingFromConcreteEng.gf

concrete GreetingFromConcreteEng of GreetingFromConcrete = open SyntaxEng, ParadigmsEng, IrregEng, Prelude in {
  lincat
    Sentence = Phr; 
    Verb = {v : V3 ; rel : Relation}; 
    Noun = {n : N ; rel : Relation};


  lin
    -- MySentence first judge if these two given variables are a good match
    MySentence action person = case <action.rel, person.rel> of {
      <RelA, RelA> => sayHi (action.v) (person.n);
      <RelB, RelB> => sayHi (action.v) (person.n);
      <_, _> => sayHi (action.v) (mkN(nonExist))
      };

    Say = {v = mkV3(say_V) (noPrep) (to_Prep); rel = RelA};
    Mike = {n = mkN("Mike") ; rel = RelA};
    John = {n = mkN("John") ; rel = RelB};


  param
    Relation = RelA | RelB;

  oper
    -- Generate the final sentence
    sayHi : V3 -> N -> Phr =
      \v, n -> mkPhr(mkUtt(mkImp(mkVP
                                  (v)
                                  (mkNP (mkN "hi"))
                                  (mkNP (n)))));
   
  }

Problem

My solution is obviously not the best to offer since it would result in generating 50% empty sentences when using the command gr | l, which would get worse the larger the parameter Relation is.

Questions

1- Is there any way to force abstract to give only matched parameters from the concrete file?
2- Or Is there any possible way to ask abstract to give another parameter when not matched from concrete instead of just generating the empty line using nonExist?


Solution

  • As for your questions, no and no.

    Better workflow in the GF shell

    If you want a less annoying workflow in the GF shell, you can gr several sentences at once, then it's more likely to get some that linearise. Like this:

    GreetingFromConcrete> gr -number=10 | l 
    say hi to Mike
    say hi to Mike
    say hi to Mike
    
    say hi to Mike
    
    
    
    
    say hi to Mike
    

    To get rid of the empty lines, you can use the ? in the GF shell to pipe an external command:

    GreetingFromConcrete> gr -number=10 | l  | ? tr -s '\n'
    say hi to Mike
    say hi to Mike
    say hi to Mike
    say hi to Mike
    say hi to Mike
    

    The tr -s removes duplicated instances of a character, in this case the line break, and you won't have empty lines in the output.

    "Overgeneration" in GF grammars

    The way you've translated the selectional restriction task from abstract syntax (dependent types) to concrete syntax (parameters) is perfectly fine, that's the standard way things are done in GF. I have suggested a similar transformation in a previous answer (scroll down to 3. Alternative way of making this grammar without dependent types).

    Often a GF grammar is embedded into a larger program. It doesn't matter if the GF grammar is overgenerating—the Python/Haskell/… program takes care that only the wanted trees are used, and none of the unwanted trees. I've written about that previously in my blog.