翻译d中的生成函数

原地址
前不久,AA大神发起完全转发讨论,发现,有时需要更改函数的属性,如加/减/挂起用户定义属性/存储类,这在我实现开放方法时也努力过.我放在bolts元编程库里面了.

元 前向(别名 函数)
{
  导入 标.特征:形参;
  静 每一(
    重载;__特征(取重载,__特征(父,函数),__特征(标识符,函数))){
    动 前向(形参!重载 实参)
    {
      中 重载(实参);
    }
  }
}

...

整 加(整 a,整 b){中 a+b;}
串 加(串 a,串 b){中 a~b;}

断定(前向!加(1,2)==3);      //通过
断定(前向!加("a","b")=="ab");

看起来不错,然而,不完美,但也不远.它涵盖了很多,包括初学者未意识到的,如前向处理以下函数时未丢弃函数属性/参数存储类:

类 矩阵{...}

矩阵 乘(域 常 矩阵 a,域 常 矩阵 b)纯 @安全
{
  中...;
}

指示(消息,的型(乘));
//纯 @安全 矩阵(域 常(矩阵)a,域 常(矩阵)b)

指示(消息,的型(前向!乘));
//纯 @安全 矩阵(域 常(矩阵) 0参,域 常(矩阵) 1参)

它甚至处理参数上的用户定义属性,用定属有两个问题:不带函数用定属对返回引用函数不工作.他们很好修复:

构 测试形参;
空 测试加(@测试形参 整 a,@测试形参 整 b);
指示(消息,的型(测试加));
//空(@(测试形参)整 a,@(测试形参)整 b)
指示(消息,的型(前向!测试加));
//空(@(测试形参)整 a,@(测试形参)整 b)

但不完全正确,如被转者@信任,转发者@安全.

元 前向(别名 函数)
{
  导入 标.特征:形参;
  静 每一(重载;__特征(取重载,__特征(父,函数),__特征(标识符,函数)))
  {
    @(__特征(取属性,函数))//复制函数用定属
    动 引用 前向(形参!重载 实参)
    {
      中 重载(实参);
    }
  }
}

因为,转发者包含调用useSysCall函数,因为调用@信任是安全的,编译器推导为@安全.

@信任 空 用系统调用(){...}
指示(消息,的型(&用系统调用));//空 函数()@信任
指示(消息,的型(&前向!用系统调用));//空 函数()@安全

然而,AA大神的问题暗示这不能同名,任务是forward!fun.fun(...)像这样,伪码如下:

//将形式化`前向!我的函数`实例
元 前向!我的函数
{
    空 我的函数(整 a,引用 双精 b,出 串 c)
    {
        中 我的函数(a,b,c);
    }
    整 我的函数(在 串 a,进出 双精 b)
    {
        中 我的函数(a,b);
    }
}

尽管差别不大,但要精确实现,就有难题.用前向,用模板名作为函数名,固定了函数名,要用一个依赖被转名字来创建函数名.只能用串插件.第一次,我:

元 前向(别名 函数)
{
  导入 标.格式:格式;
  导入 标.特征:形参;
  枚 名=__特征(标识符,函数);
  静 每一(重载;__特征(取重载,__特征(父,函数),名)){
    @(__特征(取属性,函数))
    动 引用 插件(名)(形参!重载 实参)
    {
      中 重载(实参);
    }
  }
}

但不行,串插件只能创建表达式/语句,方法是简单扩展插件来包含方法定义,q{}很好用:

元 前向(别名 函数)
{
  导入 标.格式:格式;
  导入 标.特征:形参;
  枚 名=__特征(标识符,函数);
  静 每一(重载;__特征(取重载,__特征(父,函数),名)){
    插件(q{
        @(__特征(取属性,函数))
          动 引用%s(形参!重载 实参)
        {
          中 重载(实参);
        }
      }.格式(名));
  }
}

尽管串插件很强大,但本质是,对许多d程序员,用就感觉失败了.现在搞个类似,但稍难的问题,写个类模板来模拟接口.如:

接口 可序化数格
{
  串 按数格()常;
}
空 主()
{
  动 模仿=新 模拟!可序化数格();
}

初学者可能会:

类 模拟(别名 接口):接口
{
  导入 标.格式:格式;
  导入 标.特征:形参;
  静 每一(成员;__特征(所有成员,接口)){
    静 每一(函数;__特征(取重载,接口,成员)){
      插件(q{
          @(__特征(取属性,函数))
          动 引用%s(形参!函数 实参)
          {
            //记录调用
            静 如(!是(中类型!函数==空)){
              中 中类型!函数.初化;
            }
          }
        }.格式(成员));
    }
  }
}

同样,编译失败,抛错误.

错误:函数`挑战.模拟!(可序化数格).模拟.按数格`中 类型

换句话说,这里不能用,要显式指定类型.

类 模拟(别名 接口):接口
{
  导入 标.格式:格式;
  导入 标.特征:形参,中类型;
  静 每一(成员;__特征(所有成员,接口)){
    静 每一(函数;__特征(取重载,接口,成员)){
      插件(q{
          @(__特征(取属性,函数))
          中类型!函数%s(形参!函数 实参)
          {
            //记录调用
            静 如(!是(中类型!函数==空)){
              中 中类型!函数.初化;
            }
          }
        }.格式(成员));
    }
  }
}

这不会处理引用函数,加上引用呢?

//同上
引用 中类型!函数%s(形参!函数 实参)...

接口不返回引用的函数都会失败,第一个挑战中成功的原因是我们在模板内调用包装函数,它使编译器推导出了原函数的所有特征,并复制给转发者函数.但此处没有.编译器将复制一部分(纯,@安全)函数特征来匹配重载函数,但另一部分(引用,常,其他修饰符)就没复制了.
有函数修饰符的问题,如常,不变,共享,静,还有其他方面问题.因此,只能用特征来分析函数属性,并在串插件中生成待注入(各种特征)串.

  插件(q{
      @(__特征(取属性,函数))
      %s中型!函数%s(形参!函数 实参)
      {
        //记录调用
        静 如(!是(中类型!函数==空)){
          中 中类型!函数.初化;
        }
      }
    }.格式(
        (函数属性!函数&函数属性.常_?"常":"")
      ~(函数属性!函数&函数属性.引用_?"引用":"")
      ~...,
      成员));
    }

你可在std.typecons.wrap中发现代码为存储类/修饰符处理串插件合成位.
目前研究了存储类/修饰符/用定属,而参数列表作为单个块整体传递,但有时要调整生成的参数列表.在开放方法中确实有这个需求.给定函数声明:

矩阵 乘(虚!矩阵 a,双精 b);

开放方法生成:

矩阵 分发器(矩阵 a,双精 b)
{
  中 解析(a)(a,b);
}

方法是个标记.表明要考虑(即要传递哪些给解析)哪个参数,此处只解析a.
记住,不允许分发器直接用参数类型.开放方法并没有矩阵什么的.你需要用标.特点里面的返回类型/参数来构造.
先不管函数属性/用定属,好像这样解决:

中类型!乘 分发器(
  移除虚!(形参!乘[0])a,形参!乘[1]b)
{
  中 解析(a)(a,b);
}
指示(消息,的型(&分发器));//矩阵 函数(矩阵,双精)

还有个移除虚标记型,这保留参数的存储类/用定属了吗?没有.

@无垃集 空 缩放(引用 虚!矩阵 m,懒 双精 由);
@无垃集 中类型!缩放 分发器(移除虚!(形参!缩放[0])a,形参!缩放[1]b)
{
  中 解析(a)(a,b);
}
指示(消息,的型(&分发器));//空 函数(矩阵 a,双精 b)

第1个参数的引用,第2个的丢失了.问题在于参数.该模板是是(is)操作符与__参数的包装,是个怪兽.参数列表作为整体,用它复制到另一个函数,没问题.单独处理时就有问题了.

指示(消息,形参!缩放.的串);//(引用 虚!(矩阵),懒 双精)
指示(消息,形参!缩放[0].的串);//虚!(矩阵)
指示(消息,形参!缩放[1].的串);//双精

单独返回,只返回类型,其余都丢弃了!但用切片又行.

指示(消息,形参!缩放[0..1].的串);//(引用 虚!(矩阵))
指示(消息,形参!缩放[1..2].的串);//(懒 双精)

给我们处理scale的方法.

中类型!缩放 分发器(???,形参!缩放[1..2]){...}

???里放什么,移除虚!(参数!scale[0..1])不管用,移除虚期望类型,(参数!scale[0..1])不是类型,它是包含类型/存储类/类型构造器/用定属怪物.
继续用串插件.

插件(q{
    %s 中类型!(缩放)分发器(
      %s 移除虚!(形参!(缩放)[1])a,
      形参!(缩放)[1..2]b)
    {
        解析(a)(a,b);
    }
  }.格式(
    函数属性!缩放&函数属性.无垃集?"@无垃集":""
    /*也处理其他函数属性*/,
    __特征(取形参存储类,缩放,0)));
指示(消息,的型(分发器));//@无垃集 空(引用 双精 a,懒 双精)

还不够,不能处理参数的用定属.
继续折射.只写一遍难看的,然后包装起来.放在bolts里.
入口点在折射函数模板,其取函数锚点串并返回包含函数的所有信息的不变函数对象.可在编译时使用.
函数有个(返回原函数声明的)混合属性,

矩阵 乘(虚!矩阵 a,双精 b);
指示(消息,折射!(乘,"乘").混合);
//@系统 中类型!(乘)乘(形参!(乘)0);

为什么折射需要锚点串?尽管可用__特点(标识符...)推导出函数名串,但不够.因为一般在通过别名传递函数给折射的模板中使用.模板域函数名无意义,如果存在名字,则不能用名字命名函数.对剖析函数的元式必须在标识别名本地符号中工作.如.
考虑:

模块 矩阵;
矩阵 乘(虚!矩阵 a,双精 b);
方法!乘 乘方法;//开放方法对每一`声明`方法创建`方法`对象
模块 开放方法;
构 方法(别名 函数)
{
    枚 中混合类型=折射!(函数,"函数").中型;
    指示(消息,中混合类型);            //中类型!(函数)
    插件("别名 R =",中混合类型,";");//好
    指示(消息,R.的串);                   //矩阵
}

开放方法中,无乘/矩,即使有,也不是模块的,因为会导致循环依赖,d禁止这样.但有个fun符号别名该函数,因而可用返回类型!fun表示返回类型.函数各方面都可用:

@无垃集 空 缩放(引用 虚!矩阵 m,懒 双精 由);
指示(消息,折射!(缩放,"缩放").形参[0].存储类);//["引用"]

函数也有返回有部分变化的新函数对象的方法,用来创建函数的变体,如:

指示(消息,
  折射!(缩放,"缩放")
  .带名("分发器")
  .带体(q{{解析(_0[0])(_0);}})
  .混合
);
@无垃集 @系统 中类型!(缩放)分发器(引用 形参!(缩放)[0]_0,懒 形参!(缩放)[1]_1)
{
  解析(_0[0])(_0);
}

也是折射名的来源,先模板,再修改.然后返回串,再借用插件创建新函数.
开放方法需要保留存储类时改变第1参类型.用bolts.实验.折射,这容易:

原=折射!(缩放,"缩放");
指示(消息,
  原
  .带名("分发器")
  .带形参(
    [原.形参[0].带型(
        "移除虚!(%s)".格式(原.形参[0].类型)),
     原.形参[1],
    ])
  .带体(q{{
      中 解析(_0)(%s);
   }}.格式(原.混合实参))
);

生成的代码将参数包分成独立的参数,

@无垃集 @系统 中类型!(缩放)分发器(
  引用 移除虚!(形参!(缩放)[0])_0,形参!(缩放)[1..2]_1)
{
  中 解析(_0)(_0);
}

注意,不同处理的第1和第2个参数,破坏性打开第1个参数,因为我们要替换类型,迫使我们用索引访问第1个参数,从而丢失存储类,用定属,等,所以要显式重应用他们.第2个参数就不必了,可用参数切片,确实在,在怪物内部.
初看,d的生成函数好像与lisp一样好,然而,更好控制时,像在写,实际上必须用串插件.
bolts实验.折射,提供更聪明,更轻松方式来生成函数,其内部隐藏了串插件细节.
作者:Jean-Louis Leroy,比利时人,关注orm,开放方法,dsl,一般语言发展.

posted @   zjh6  阅读(6)  评论(0编辑  收藏  举报  
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示