Search code examples
gf

Proper names with determiners in GF


I'm trying to generate the following sentence in GF: Jack wants to listen to a Taylor song But as I can see in RGL PN -> NP is the only solution to get a proper name. How to get GF to output a proper noun with a determiner and an object.

Also I'm wondering why in RGL PN is being called proper name instead of proper noun?


Solution

  • The syntactic function of "Taylor" in that sentence is a modifier to "song". So the grouping isn't "a Taylor", but rather "a song", with Taylor as a modifier:

    • a song
    • a good song
    • a Taylor song

    This answer doesn't necessarily work in other languages than English. But there are plenty of other structures you can use straight out of RGL, like "a song by NP".

    Limited set of artists

    If you have a fixed set of artists, it's probably easiest to just cheat and make them into adjectives in English. Here's an example:

    abstract Song = {
      flags startcat = Sentence ;
      cat
        Sentence ;
        Person ;
        Artist ;
    
      fun
        wantToListen : Person -> Artist -> Sentence ;
        Jack : Person ;
        Taylor, Ariana : Artist ;
    }
    

    For the English version, we make the lincat of Artist into AP. This works in English, because the adjectives don't inflect, so it'll just be a single string.

    concrete SongEng of Song = open SyntaxEng, ParadigmsEng, LexiconEng in {
    
      lincat
        Sentence = Utt ;
        Person = NP ;
        Artist = AP ; -- cheat in English
    
      lin
        -- : Person -> Artist -> Sentence ;
        wantToListen person artist =
          let artistsSong : CN = mkCN artist song_N ;
              listenToSong : VP = mkVP listen_V2 (mkNP a_Det artistsSong) ;
           in mkUtt (mkS (mkCl person want_VV listenToSong)) ;
    
        -- : Person
        Jack = mkNP (mkPN "Jack") ;
    
        -- : Artist
        Taylor = mkAP (mkA "Taylor") ;
        Ariana = mkAP (mkA "Ariana") ;
    }
    

    Works as intended:

    $ gf SongEng.gf
    …
    Song> gt | l -treebank
    Song: wantToListen Jack Ariana
    SongEng: Jack wants to listen to an Ariana song
    
    Song: wantToListen Jack Taylor
    SongEng: Jack wants to listen to a Taylor song
    

    Note that if your bands have an article of their own, you'll get Jack wants to listen to a The Beatles song.

    Artist as modifier vs. subject

    You can always make a more complex lincat for Artist, here's an example. We'll add TheBeatles as an Artist in the abstract syntax. I also add another function, where the artist is not just a modifier, but the subject.

    abstract Song2 = Song ** {
      flags startcat = Sentence ;
      fun
        TheBeatles : Artist ;
        isGood : Artist -> Sentence ;
    }
    

    And here's the concrete. I'm not reusing the original SongEng, because I need to change the lincat of Artist.

    concrete Song2Eng of Song2 = open SyntaxEng, ParadigmsEng, LexiconEng in {
    
      lincat
        Sentence = Utt ;
        Person = NP ;
        Artist = LinArtist ; -- {independent : NP ; modifier : AP}
    
      lin
        -- : Person -> Artist -> Sentence ;
        wantToListen person artist =
          let artistsSong : CN = mkCN artist.modifier song_N ;
              listenToSong : VP = mkVP listen_V2 (mkNP a_Det artistsSong) ;
           in mkUtt (mkS (mkCl person want_VV listenToSong)) ;
    
        -- : Artist -> Sentence
        isGood artist = mkUtt (mkS (mkCl artist.independent good_A)) ;
    
        -- : Person
        Jack = mkNP (mkPN "Jack") ;
    
        -- : Artist
        Taylor = mkArtist "Taylor" ;
        Ariana = mkArtist "Ariana" ;
        TheBeatles = mkArtist "The Beatles" "Beatles" ;
    
      oper
        LinArtist : Type = {independent : NP ; modifier : AP} ;
        mkArtist = overload {
          mkArtist : Str -> Str -> LinArtist = \thebeatles, beatles -> {
            independent = mkNP (mkPN thebeatles) ;
            modifier = mkAP (mkA beatles)
            } ;
          mkArtist : Str -> LinArtist = \taylor -> {
            independent = mkNP (mkN taylor) ;
            modifier = mkAP (mkA taylor)
            }
          } ;
    }
    

    And here's the output from that.

    Song2> gt | l
    Ariana is good
    Taylor is good
    The Beatles is good
    Jack wants to listen to an Ariana song
    Jack wants to listen to a Taylor song
    Jack wants to listen to a Beatles song
    

    If you wanted to make "The Beatles" into plural, then we can do this. Add another overload instance of mkArtist, where you give it an already constructed NP. Then you can specify that "The Beatles" are, in fact, plural.

      oper
        mkArtist = overload {
          mkArtist : Str -> Str -> LinArtist = … ; -- same as before
          mkArtist : Str -> LinArtist = … ; -- same as before
          mkArtist : NP -> AP -> LinArtist = \thebeatles,beatles -> {
            independent = thebeatles ;
            modifier = beatles
            }
      lin
        TheBeatles =
          mkArtist
            (mkNP aPl_Det (mkN "The Beatles" "The Beatles"))
            (mkAP (mkA "Beatles")) ;
          } ;
    

    And this gives you the following output:

    Song2> l isGood TheBeatles 
    The Beatles are good
    

    Arbitrary strings as artists

    Finally, if you want to use arbitrary strings as artists, you can use string literals. For the independent field, it works great: there are functions in the Symbolic module that take a string literal to NP.

    However, for the modifier field, you need to go outside the API---here are instructions how to do it in a slightly less unsafe manner, but it's still not guaranteed to be stable, if the RGL internals ever change.

    Disclaimers aside, here's the final extension.

    abstract Song3 = Song2 ** {
      flags startcat = Sentence ;
      fun
        StringArtist : String -> Artist ;
    }
    

    And the concrete. This time we do extend Song2Eng, because there's no need to change the lincats.

    concrete Song3Eng of Song3 = Song2Eng ** open SyntaxEng, LexiconEng, SymbolicEng in {
      lin
        -- : String -> Artist ;
        StringArtist string = {
          independent = symb string ; -- symb : String -> NP, from SymbolicEng
          modifier = <mkAP good_A : AP> ** {s = \\_ => string.s} -- hack
          } ;
    }
    

    Let's see how it works:

    Song3> p "Jack wants to listen to a skhdgdjgfhjkdfhjsdf song"
    wantToListen Jack (StringArtist "skhdgdjgfhjkdfhjsdf")
    
    Song3> p "9ortge94yhjerh90fpersk is good"
    isGood (StringArtist "9ortge94yhjerh90fpersk")
    

    Just a warning: it works perfectly fine to linearise strings with spaces, so this is ok:

    Song3> l isGood (StringArtist "Spice Girls")
    Spice Girls is good
    

    But you can't parse string literals with spaces.

    Song3> p "Backstreet Boys is good"
    The parser failed at token 2: "Boys"