什么叫你的母语是Kotlin?
前言
本文需要的前置知识:
- OOP
- Kotlin语法
- 特别简单的人类自然语言语法知识
我们都知道,OOP的经典范式是每个方法或者属性都需要有对应的对象。一个很常见的例子就是 someInstance.doSomething(someTarget) :某个对象执行一个成员函数,参数是另一个对象。
笔者某日突发奇想,这不是和人类说话方式很像吗?在英语中我们说: subject verb object ,例如 I(S) eat(V) fish(O) ,能不能将他和OOP概念对应呢?将someInstance对应S,doSomething对应V,someTarget对应O,也就是 I eat fish <==> I.eat(fish) 。本文就尝试用Kotlin富有表现力的语法设计了用“编程语言说人话”的系统。就叫他Kotlang吧。
设计目标
- 一个语法正确的句子应当对应一个Kotlang的类型正确的项。
- 表达方式尽可能自然
- 采用上述的OOP转换思想
为什么用Kotlin?
Kotlin是一个OOP语言,而且表达能力丰富,有大量语法糖,本身就是写DSL的好手。类型系统完善,可以通过类型推断该写怎么样的语法。因此非常适合用于尽可能贴近自然语言
当然,设计方式不同会导致得出的结论不同,这里只给出我的一种设计方式。
1. 设计名词
非常简单的思想,名词是个类,具体名词是具体对象。我们还可以用接口表示一些概念。
interface Destroyable
open class Noun(val word:String)
object I:Noun("I") //为了方便,人称代词也可以是名词
object You:Noun("You")
object Box:Noun("Box"), Destroyable
object Dog:Noun("Dog")
2. 设计动词
首先,动词的类型(type)应该是什么?本文为了方便起见,只讨论只有最多一个宾语的动词(也就是没有给、送、让之类的)。
我们先从现实中的句子来看:动词只要给他一个主语和一个宾语,就可以形成句子,所以它可能是一个函数,需要两个参数。或者,从刚刚的OOP转换思想更直接能够看出动词的类型是:
open class Clause //句子的类
typealias Verb<T> = Noun.(T)->Clause //Subject.Verb(Object)
也就是,只要给一个receiver Noun当主语,一个参数当宾语,就能够变成句子。但是等等,什么是泛型T??
众所周知,动词常常对需要的宾语有一些限制。比如,”喝”的宾语肯定要能喝(Drinkable)吧(饮料一定要能喝✍✍✍!),像”hope”之类的词可能他的“宾语”还是一个句子呢(I hope you can go)。这就是T的作用。
Yeah!我们现在就能写出第一个句子啦,可以和你的程序员女友说:
val<T> love : Verb<Noun> = TODO() // 爱
val firstSentence = I.love(You) //我爱你,类型是Clause。
3. 修饰动词
ohno,不好,我和女友谈崩了。现在我要说“我不爱你”了,怎么办?为了表达“不”的概念,我们需要引入动词的修饰功能(注:本文将副词和时态统称动词的修饰,这在自然语言语法上是不成立的,自然语言通常有更多限制禁止某些修饰同时出现)。动词的修饰本质上就是函数的修饰,这是一种Decorator Pattern。根据类型推断,我们可以得到下面的否定修饰:
val<T> Verb<T>.negate:Verb<T>
get() = TODO()
- 为什么TODO
因为本文只关注“语法”,具体的TODO内容是”语义“部分,你可以根据实际需求(例如实际功能是翻译Kotlang到英语或者是进一步检查语法)修改里面的值
然后就可以表达我不爱你咯
I.love.negate(You) //❌然而不是这么写的
I.(love.negate)(You) //是这么写的
简单简单!我们可以同样方式做出其他的一些修饰:
val<T> Verb<T>.past:Verb<T> //过去时
get() = TODO()
val<T> Verb<T>.future:Verb<T> //将来时
get() = TODO()
我们就可以写出:
I.(love.past)(You) //我曾经爱你
I.(love.negate)(You) //……我现在不爱了
4. 形容词
形容词是修饰名词的词。
冷知识:不是所有语言都有形容词。许多语言的形容词可以用动词担当,例如某些语言可能有“是红色”这个动词。而在英语中形容词可以通过从句的方式表达。
例如:假设”is red”是个动词(其实不是),那么”The dog is red”这个句子就是“狗是红色的”。”The dog THAT is red”,就是将句子转化成从句,用that来将句子修饰狗,意思就是红色的狗啦。
这就意味着,我们需要实现从句:
5. 从句
在英语和其他人类语言中,从句存在一个叫Filler-gap dependency的东西。我们都知道,从句(主语从句、定语从句)需要通过”what/who/which/that”来打头,这个就叫filler;而(英语)从句本身并不是一个完整的句子,他一定少了某个部分,这就是gap。而filler正如其名其实就是gap的“替身”,因为他要充当gap的语法成分,而且语义上也是同一个对象,这就是“filler-gap dependency”。有点复杂吧?我们直接上例子:
The dog that is red //Filler: That
从句是: ____ is red // 不是完整的句子
从句还原成完整的句子应该是: (The dog) is red //也就是说that指代的是从句里的主语
The Gensokyo which every Touhou enjoyer say they love //Filler: Which
从句是: every Touhou enjoyer say they love ____ //少love的宾语
从句还原成完整的句子应该是: every Touhou enjoyer say they love (Gensokyo)
主语从句的例子:
What we believe in every day might be wrong //Filler: What
从句是: we believe in ___ every day //少believe in的宾语
但是gap这件事在编程语言里很不友好,因为我们很难类型正确的表达一个”空格“。至少在Kotlin里,下面的语法是非法的:
val iLoveSomeone = I.love //试图获取函数引用,但是不行
幸运的是,在英语和很多其它语言中还有一个表达从句的方式,叫做resumptive pronoun。在英语中是这么用的:
This is the camel [that I think *it* likes Oscar]
This is the friend that I don't know *their* address
// 注:有的人可能觉得这些句子怪怪的,也不是所有英语母语者都能接受
看懂了吗?就是用一个代词去explicit的替代这个gap,而kotlin中的”it”语法感觉就是为这个诞生的!
我们将从句与一个需要参数的lambda相对应。这下可以写出如下函数:
fun <T : Noun> T.that(what: (T) -> Clause): T { //定语从句
TODO()
}
fun what(subclause: (Noun) -> Clause): Noun = TODO() //主语从句
现在我们已经能说一些很复杂的句子了:
val be: Verb<Noun> = TODO()
what{
I.love(it)
}.be(
what{
You.(be.past)(it)
}
)
//直译: What I love is what you were
//我喜欢的是过去的你
You.that{
I.(love.past)(it)
}.(be.negate)(You)
//直译: You that I loved is not you
//我喜欢过的你不是(现在的)你
宾语从句
宾语从句我们可以换一种思路,利用Verb的泛型。也就是Verb需要一个句子作为宾语嘛:
typealias Subclause = (Noun) -> Clause
val want: Verb<Subclause> = TODO() //注意泛型类型
I.want{
it.love(You)
} //I want to love you <==> I want that (I) love you
//不过因为没有gap了为什么不直接说
I.want{
I.love(You)
}
6. 连词和所属
我们继续扩充我们的语法,还可以复习一下Kotlin特色的infix函数
infix fun <T> T.of(n: Noun): T = TODO()
infix fun <T> T.and(other: T): T = TODO()
infix fun <T> T.but(other: T): T = TODO() //注:语言学上but不一定视为连词。方便起见我们这么写
I.(love.past)(You and Dog of You) but I.(love.negate)(You)
//我曾爱着你和你的狗,但是我现在不爱你了
7. 时间和地点
我们可以为句子指定时间或者地点,例如”在哪里“、”在什么时候“,这类信息叫做adjunct,他们是可选的而且在语言学上被视为动词短语的一个成分。嘛,在Kotlang里因为没有”动词短语“的概念(因为没办法脱离对象谈函数),我们可以视为“动词”的一个组成,这就又变成了动词修饰。当然,也有其他的设计方法,例如将其看成动词短语的一部分。
interface Timeable //可以充当时间的玩意儿
object Now: Noun("Now"), Timeable
object Tomorrow: Noun("Tomorrow"), Timeable
open class Clause: Timeable
infix fun <T> Verb<T>.at(time: Timeable): Verb<T> = TODO()
I.(love at Now)(You of Tomorrow) //我现在爱着明天的你
I.(love at You.love(I))(You) //我在你爱我的时候爱你
8. 接下来呢?
我们已经开始逐步构建这个语言的骨架了。如果我们要设计一门完整的语言,我们还有很多课题要做,他们有的是简单的,有的可能是复杂的:
- 被动,使役等语法概念
- 名词化(例如”To be or not to be, that is a question“)
- 冠词(a/the)
- 同位语
- 强调、祈使和语序变化
- 疑问句
- 人类语言都没有的语法功能??
感兴趣的读者可以进一步拓展,或者尝试用这门”语言“造造句子,写写作文。这不是很酷的一件事嘛?
9. Kotlang的语言学
最后我们来用自然语言的语言学简单分析一下Kotlang。Kotlang有一些人类语言不同的概念:
- 人类语言中存在VP(动宾短语)。VP 就是以动词为核心的短语,可以往里塞补足语、附加语。比如英语 “eat fish in the kitchen”,在句法树里可能构成一个 VP,里面包含了动词、宾语和地点状语。通常,一个VP一般可以用”do so”这种词语替换(不准确的说法),表示一个动作。研究表明,几乎所有人类都会潜意识里提取句子中的VP并或多或少将他们看成一个整体。但在 Kotlang 中,你需要通过模仿impersonal构造才能构造出一个类似独立于主语的“动词短语”构造,不是那么自然。实质上所有的动词都依附于一个名词(
Noun)作为扩展函数,只有和主语结合时才会出现。同样PP等其他语法结构都不能独立存在了。给出impersonal构造如下:
typealias VP = Noun.() -> Clause fun<T> Verb<T>.impersonal(n:T): VP = TODO() val vp=love.impersonal(You) I.(love.impersonal(You))() //Use VP - 人类语言有Head的概念,Head是一个短语/句子的”中心词“,或者说最重要的词。在人类语言中普遍认为动作是一个句子的Head,但是Kotlang中动词的地位明显没有那么高。
- 人类语言在从句中常常出现Island Effect。Island Effect是指并不是所有的地方都可以挖”gap”,例如有些语言只允许词作为主语出现在定语从句中而不能作为定语从句的宾语。而Kotlang非常自由,可以随意插入还不会引起任何歧义。
- 人类语言的动词常常有多个valence。valence是指动词需要的名词数量(例如不及物动词为1,及物动词为2,送、给、让等动词一般为3……),而Kotlang中valence一定是2。当然,你可以实现不是2的valence,通过改变Verb的泛型T的参数为
Pair或者Unit之类的。 - 人类语言常常有大量的特例,格和agreement。Kotlang一切都没有。例如在英语中,你需要说
I love you, you love me(不是you love I),而Kotlang就没有这种不必要的特例。但是,同时因为缺少格的概念,也没法自由转换语序和词序。 - Kotlang是一门严格的SVO语言,与世界上约40%的语言语序一致,合乎周礼!
10. 未曾设想的路线
其实,在实现动词的时候,我们有着另一种实现方式。Kotlin允许我们获得一个成员函数的指针,按照如下方式:
myObject::myMethod // 类型是myMethod的类型
这是一个主语和动词的绑定,代表了一个类似“主谓结构”。我们也可以用它来给动词上修饰:
I.eat(Apple)
I::eat.negate(Apple) //注意.变成了::
这样做有意思的一点是:研究表明,人类语言认知中常常不会将主语和动词绑定,而是动词和宾语绑定。也就是说这么做是和人类直觉相反的。而且由于 :: 的特性,我们也永远无法将动词和主语分离了(动词无法单独出现),这可能会带来一些非常神奇的影响。感兴趣的读者可以进一步讨论。
版权声明:
作者:XGN
链接:https://blog.hellholestudios.top/archives/2058
来源:Hell Hole Studios Blog
文章版权归作者所有,未经允许请勿转载。

共有 0 条评论