如何在Perl 6中为参数类型创建自定义元模型?

Kai*_*epi 6 types metaprogramming perl6 parametric-polymorphism raku

我正在编写一个用于在Perl 6中创建monad的库。由于仅使用常规参数角色就无法正确扩展monad(据我所知),因此我决定创建一个自定义元模型类,该类扩展Metamodel::ParametricRoleHOW并引入特殊语法来创建单子。问题是当我尝试写时monad MonadName[::T],会发生这种情况:

bastille% perl6 monad-test.p6
===SORRY!=== Error while compiling /home/morfent/Documents/p6-Monad/monad-test.p6
Unable to parse monad definition
at /home/morfent/Documents/p6-Monad/monad-test.p6:8
------> monad Identity?[::T] {
    expecting any of:
        generic role
Run Code Online (Sandbox Code Playgroud)

当我只写monad Identity { ... }时,尝试专门化类型时会抛出该错误。我该怎么写,使单子参数化?单子不具备准确扩展角色元模型,但我宁愿它是可以使用does与单子,而不是is。另外,即使我已经知道如何执行此操作,其他人也可能没有,因为我认为它没有记录在案,因此,如果您可以在回答中包括如何使任何类型的参数成为一般参数,那么这将是一个更好的参考。

这是我目前用于创建monad的代码:

use v6.d;

class Type::MonadHOW is Metamodel::ParametricRoleHOW {
    has Int $!feature_flags;

    my enum FeatureFlags (
        Value  => 0x1,
        Bind   => 0x2,
        Then   => 0x4,
        Return => 0x8
    );

    method !set_value(\M, Attribute:D $attr) {
        self.Metamodel::ParametricRoleHOW::add_attribute: M, $attr;
        $!feature_flags +|= Value;
        self
    }

    method !set_bind(\M, &bind) {
        self.Metamodel::ParametricRoleHOW::add_method: M, 'bind', &bind;
        $!feature_flags +|= Bind;
        self
    }

    method !set_then(\M, &then) {
        self.Metamodel::ParametricRoleHOW::add_method: M, 'then', &then;
        $!feature_flags +|= Then;
        self
    }

    method !set_return(\M, &return) {
        self.Metamodel::ParametricRoleHOW::add_method: M, 'return', &return;
        $!feature_flags +|= Return;
        self
    }

    method new_type(|args) {
        my \M = self.Metamodel::ParametricRoleHOW::new_type: |args;
        M.HOW.HOW.get_attribute_for_usage(M.HOW, '$!feature_flags').set_value(M.HOW, 0);
        M
    }

    method add_attribute(\M, Attribute:D $attr) {
        given $attr.name {
            when '$!value' { self!set_value: M, $attr }
            default        {
                self.Metamodel::ParametricRoleHOW::add_attribute: M, $attr
            }
        }
    }

    method add_method(\M, $name, &method) {
        given $name {
            when 'bind'   { self!set_bind:   \M, &method }
            when 'then'   { self!set_then:   \M, &method }
            when 'return' { self!set_return: \M, &method }
            default       {
                self.Metamodel::ParametricRoleHOW::add_method: \M, $name, &method
            }
        }
    }

    method compose(\M, :$compiler_services) {
        die "Monads must implement a private value attribute."
            unless $!feature_flags +& Value;
        die "Monads must implement a bind method."
            unless $!feature_flags +& Bind;
        die "Monads must implement a then method."
            unless $!feature_flags +& Then;
        die "Monads must implement a return method."
            unless $!feature_flags +& Return;
        self.Metamodel::ParametricRoleHOW::compose: M, :$compiler_services
    }
}

subset Monad is export where *.HOW.WHAT ~~ Type::MonadHOW;

proto sub infix:«>>=»(|) is export {*}
multi sub infix:«>>=»(Monad:D $monad, Mu &f) {
    $monad.bind: &f
}

proto sub infix:«>>»(|) is export {*}
multi sub infix:«>>»(Monad:D $monad, Monad:D $next) {
    $monad.then: $next
}

proto sub pure(|) is export {*}

my package EXPORTHOW {
    package DECLARE {
        constant monad = Type::MonadHOW;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我的Monad子集也可以写成参数的(理想情况下无需创建另一个自定义元模型类),那也会很有帮助。

编辑:我说将monad实施为角色而不是为其编写自定义元类时不能扩展monad的原因是,因为在Maybe不复制代码的情况下不可能编写总和类型(例如)的monad 。使用角色Monad[::T]将如下所示:

role Monad[::T] {
    has T $!value;

    method ACCEPTS(::?ROLE: ::?ROLE $m --> Bool:D) {
        $!value ~~ $m.^get_attribute_for_usage('$!value').get_value($m)
    }

    submethod BUILD(::?ROLE: T :$!value) {}

    method return(::?ROLE: T --> ::?ROLE)               { ... }
    method bind(::?ROLE: Callable[::?ROLE] --> ::?ROLE) { ... }
    method then(::?ROLE: ::?ROLE --> ::?ROLE)           { ... }
}
Run Code Online (Sandbox Code Playgroud)

例如,这就是使用Monad[T]以下方式实现标识monad的方式:

role Identity[::T] does Monad[T] {
    method return(Identity: T $value --> Identity:D) {
        self.bless: :$value
    }
    method bind(Identity:D: Identity:D &f --> Identity:D) {
        &f($!value)
    }
    method then(Identity:D: Identity:D $m --> Identity:D) {
        $m
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,对于一个Maybemonad,您希望能够像这样拥有Just[T]Nothing[T]扩展它:

role Maybe[::T] does Monad[T] {
    method return(Maybe: T $value --> ::?ROLE) {
        ::?ROLE[T].bless: :$value
    }
    method bind(Maybe:D: Maybe:D &f --> Maybe:D) {
        &f($!value)
    }
    method then(Maybe:D: Maybe:D $m --> Maybe:D) {
        $m
    }
}
role Just[::T]    does Maybe[T] {}
role Nothing[::T] does Maybe[T] {}
Run Code Online (Sandbox Code Playgroud)

这里的问题是,因为方法存根使用::?ROLEMonad[T],这是不行的,而不存在某种方式单子和常规角色之间进行区分。有了用于monad的自定义元类,并引入特殊语法以使用/ monad通过包声明符在对象中声明它们并将它们作为目标,您可以将其定义为角色,然后能够实现,并且可以作为monad来实现。$?MONAD::?MONADMaybe[T]Just[T]Nothing[T]Maybe[T]

rai*_*iph 5

更新。请参阅此nanswer下面的评论。三个重要的事情是:1 @Kaiepi(问这个问题的人)已经知道一个问题的答案;2他们同意这个问题,因为它的意思是“非常广泛”;以及3他们和我现在投票决定结束这个问题。

如何在Perl 6中为参数类型创建自定义元模型?

这是一个非常模棱两可且非常广泛的问题。是否合理地负责?1个


在问题的正文中,您添加了详细信息以缩小可能性,这虽然很好,但是仍然非常广泛,此外,它似乎还揭示了:

  • 您可能不需要创建自定义元模型。

  • 如果确实需要这样做,那么您就会知道该怎么做。

  • 您真正想做的是修改P6语法

本答案的其余部分解释了我的意思,并试图阐明并进一步缩小问题的范围。也许这将是回答您问题的先决条件。但是目前,我怀疑是否会有一个可接受的答案,除非答案是您或乔纳森或该级别的其他人提供一些非常通用的指导。

仅使用常规参数角色就无法正确扩展monad(据我所知)

是啊

也就是说,为了提供关键信息来回答您的问题和/或减少我们不必要地降低XY问题的“ Y”分支的机会,请解释一下为什么您认为您无法“适当扩展单子”。或者,您所说的“扩展”,“单子”和“适当”是什么意思。

阅读您的问题几次后,我目前的猜测是:

  • “ monads”仅表示您使用monad声明符创建的P6构造函数;

  • “扩展”仅表示“继承自ParametricRoleHOW

  • “适当”只是意味着“以某种方式没有我在答案中显示的问题”。

但是对我来说,我的猜测是正确的并不明显,所以我想确认一下。

如果我的猜测是错误的,那么请考虑编辑您的问题,以将这个“正确扩展monads”问题简化为根据Wikipedia和/或您在标准P6语言中看到的某些特定弱点的monad定义

我决定创建一个可扩展的自定义元模型类 Metamodel::ParametricRoleHOW

您的示例代码显示您已执行了决定。:)

因此,相对于标题中的问题,我写道:“如果您确实需要这样做,那么您就会知道该怎么做”。(然后我读到您“已经知道该怎么做了。”让我困惑。)

另一方面,我假设,如果您确实需要创建自定义元模型类,则它可以仅继承自Metamodel::ParametricRoleHOW1因此,元模型方面实际上与您所显示的问题无关。

如果相反,您需要创建另一种参数类型而不是从P6当前拥有的最明显的参数类型(角色)继承,那么那就是整个“蜡球”。到那时,我认为我们处于“范围太广-如果您的问题可以用整本书来回答”的领域,那么您可能只需要编写它,也许与@JonathanWorthington共同撰写。:)

更新。我忘记了普通的老式课程是^parameterize可以的。此外,@ Kaiepi本身就是向我们介绍SO的一种相对简单的方法,即通过我在该nanswer下方的评论中所说的来将其参数化为任何类型,应该称为“ @Kaiepic构造函数”。(但是,这一切都不能阻止元模型方面与他们在问题中所显示的问题本质上无关。)

并介绍特殊语法

到目前为止,是您遇到的问题的核心。

用于创建单子

到目前为止,方面基本上与您所显示的问题无关。

问题是当我尝试写时monad MonadName[::T],会发生这种情况:

因此,提醒一下,如果事实证明原来的SO是XY,则可能是“ Y”问题-无用的切线。

Unable to parse monad definition
Run Code Online (Sandbox Code Playgroud)

为“无法解析”搜索rakudo源直接导致一行代码

        || <.panic("Unable to parse $*PKGDECL definition")>
Run Code Online (Sandbox Code Playgroud)

此行出现在P6语法package_def规则中。

但是,实际上特定的规则和界限并不重要。关键点是您是否已经知道这是怎么回事-您的代码修改了语法(通过引入monad包声明符),但修改得还不够

这与元模型,自定义或其他无关。

如果您没有意识到自己对语法的修改不够充分,那么这就是您的核心问题,您的问题应该集中在这个问题上。如果您确认是这样,那么也许我们应该在编辑问题时关闭您的问题,甚至保持关闭状态,以便您可以针对一个或两个新问题再试一次:

  • 您可以使用角色来实现想要实现的目标吗?

  • 如果不能,那么谁来写P6元编程书籍,那么在P6中实现monad的运行示例是否是一个不错的选择?

更新。现在看来,@ Kaiepi想到的问题比我所看到的要简单,希望以后再向我们展示。

即使您确实意识到问题的核心是您对语法的修改不够充分,上述与关闭,编辑/重新打开或替换问题有关的建议仍然适用。


我对语法的立即反应是要注意语法中的几个实例'role'

  • token package_declarator:sym<role> {...};

  • 的许多重复$*PKGDECL ... eq 'role'package_def包含规则panic

Aiui,您的代码注入了一个monad程序包声明符,但没有安排其程序包带有name 'role'。这样'role'package_def代码中的所有处理都会被绕过。请注意,我不建议你不要安排您的声明符重用'role'。我也不建议您也不要。我只是指出,就目前而言,我想您的代码将忽略这个至关重要的问题。


当我只写monad Identity { ... }时,尝试专门化类型时会抛出该错误。

我得到:

Monads must implement a private value attribute.
Run Code Online (Sandbox Code Playgroud)

这是您自己的代码生成的错误。

因此,我什至对您提到自己的做法感到困惑。

即使我已经知道如何执行此操作,其他人也可能不知道,因为我不认为它已被记录在案,因此,如果您可以在回答中包括如何使任何类型的参数成为一般参数,那么这将成为更好的参考。

我对此评论感到困惑!

如果您在这里正在写“回答自己的问题-以Q&A方式分享您的知识”问题,那就太好了。:)(但是我希望在编写此nanswer之前已经意识到这一点!)

这是我目前用于创建monad的代码:

我考虑过要添加一些有关您的代码的注释。我一般没有P6元编程经验,也没有特别复杂的角色元模型代码,但我知道一些基本知识,并认为我会了解您的代码在做什么。我仍然认为,如果我将其投入足够长的时间,我会做的,但是鉴于您想要的其他不确定性,并且您的代码又长又复杂且无法解释,我觉得我已经将所有精力留在了这里关键是成为橡皮鸭的能力。所以,嘎嘎?(即,请按照橡皮鸭调试说明

如果我的Monad子集也可以写成参数的(理想情况下无需创建另一个自定义元模型类),那也会很有帮助。

.o(这将是一本很棒的书)

更新。@Kaiepi的评论让我想起了一个他们已经创建的相对简单(不到10个LoC)的解决方案。请参阅下面的评论。

脚注

1 P6是任意可扩展的。所以名义上问题的字面解释将包括一个新的创造任何一种参数类型或使用现有的唯一一种在P6参数类型,即中的角色。(更新。呃,或者只是一个普通的class使用^parameterize。看到答案的身体和评论下面其他相关的评论。)更一般地说,有名无实的问题是广义的赫克!可能是因为您的monad项目需要这样的问题。也许,无论单子如何,我们都需要一个答案来使P6人员能够为参数类型创建自定义元模型。也许是时候确定与Rakudo对抗的P6元模型了执行它。也许这是一个非常广泛的问题,但是一个不错的答案只需要一本书中只有很少的几,而无需整本书。但是我怀疑,“太广泛”适用于另一种方式。