跳到主要内容

weekly 2024-07-15

· 阅读需 6 分钟

MoonBit 更新

  • 新增级联运算符(cascade operator):MoonBit 引入了 “..” 操作符,能够优雅地对可变 API 进行链式调用,同时保持可变 API 签名的整洁(依然返回 Unit)。

如图例,为了避免重复键入上图中的array, 和区分不可变与可变操作,MoonBit引入了级联运算符。对于一个类型中的所有返回Unit的方法,可以使用..将这些方法的调用串联起来,而不需要专门修改这些方法的返回类型。 array..push(5)..sort()相当于依次调用了可变操作array.push(5)array.sort(), 最终返回array

cascade.png

  • 正式移除了@package.Type::[...]的语法。推荐使用@package.of([...])作为替代。

  • 现在--target js下调试模式生成的sourcemap支持names表。因此使用 devtools 或 vscode 进行调试时,局部变量会显示为原变量名,还可以在Debug console中使用原变量名访问它。这极大的改进了调试体验。

Before: names_before.png

After: names_after.png

  • 数组支持展开语法 (spread syntax),可以在数组构造时,将任何支持iter()方法的对象表达式在语法层面展开成一组 Iter[_] 对象。

    数组表达式:

let u1 = [1, 2, 3]
let u2 = [5, 6, 7]

fn main {
let v = [..u1, 4, ..u2, 8]
println(v)
}

// [1, 2, 3, 4, 5, 6, 7, 8]

Map表达式(返回键值对):

  let map : @sorted_map.T[Int,String] = @sorted_map.of([(3, "c"), (2, "b"), (1, "a")])

fn main {
let v = [..map, (4, "d")]
println(v)
}

// [(1, a), (2, b), (3, c), (4, d)]
  • 支持了和C语言类似的位运算符(^, &, |, <<, >>),优先级同C一致。同时为了避免歧义,当表达式同时存在几个运算符并且不容易区分优先级时,formatter会插入额外的括号以改善可读性。

  • 支持重载整数字面量。在已知类型时,Int 以外的类型的字面量可以省略 UL 等特殊标记:

fn main {
let uint : UInt = 42
let int64 : Int64 = 42
let double : Double = 42
}

标准库(moonbitlang/core)更新

  • Hash Trait 即将更新为如下形式。
trait Hash {
hash_iter(Self, Hasher) -> Unit
hash(Self) -> Int
}

impl Hash with hash(self) {
let hasher = Hasher::new()
hasher.hash_iter(self)
hasher.finalize()
}

构建系统更新

测试机制调整说明

为优化黑盒测试的开发体验,moon 现支持自动将以 _bbtest.mbt 结尾的源代码文件封装为黑盒测试,moon 在编译 *_bbtest.mbt 时会自动将其所在的包作为依赖。为了更好地开发和测试 MoonBit 项目,接下来将对 MoonBit 项目的测试机制进行调整。

Background

目前一个 MoonBit 项目中可以有三种类型的测试:白盒测试(white box test)、黑盒测试(black box test)、内部测试(inline test)。

  • 白盒测试:写在 *_test.mbt 中,构建系统会把当前包中的 *.mbt*_test.mbt 一起打包编译,因此在 *_test.mbt 中可以访问当前包的私有成员,这些测试可以使用 moon.pkg.jsonimporttest-import 字段中的依赖。test-import 只在白盒测试中用到,不会打包到最终的构建产物中。

  • 黑盒测试:写在 *_bbtest.mbt 中,构建系统会在编译 *_bbtest.mbt 时会自动将其所在的包作为依赖,*_bbtest.mbt只能访问其所在包的公开成员(即模拟外部用户在使用这个包时的视角),这些测试可以使用 moon.pkg.json 中的 bbtest-import 字段中的依赖(以及其所在包,不需要显式写在bbtest-import 字段中)。bbtest-import 只在黑盒测试中用到,不会打包到最终的构建产物中。

  • 内部测试:可以直接写在 *.mbt(注意这里的 *.mbt 不包含上面提到的 *_test.mbt*_bbtest.mbt)中,可以访问当前包的私有成员,这些测试只使用 moon.pkg.jsonimport 字段中的依赖。

Change

一些命名上的调整:当前的*_test.mbt(白盒测试)后续将调整为*_wbtest.mbt,*_bbtest.mbt(黑盒测试)将调整为 *_test.mbt,鼓励大家写黑盒测试。

工具链更新

  • moonfmt改进

    • 现在当源码中存在语法错误时,格式化会忽略相关的代码,此时仍然能够执行代码格式化。

    • 当表达式同时存在几个运算符并且不容易区分优先级时,格式化会插入额外的括号以改善可读性。

  • IDE 增加对黑盒测试(*_bbtest.mbt)的支持

weekly 2024-07-08

· 阅读需 4 分钟

MoonBit 更新

  • 【重大更新】修改 array slice 的语法,从 arr[start..end] 修改为类似 Python 的 arr[start:end]。这是为了避免和接下来要支持的 cascade method call x..f() 产生语法冲突。旧的语法会在近期删除。

  • 【Wasm后端重大更新】将 fn init 中的代码编译到 start section。 之前版本中的 fn initfn main 代码块中的代码都会被编译到一个特殊函数中,这个特殊函数会被导出为 "_start" 函数,因此宿主环境需要调用 "_start" 函数来进行初始化和执行 main 函数 新版本中会使用 wasm 标准中的 start section 来存放 fn init 中的代码,从而在加载 wasm 模块的过程中完成初始化,这意味着 fn init 中代码的执行不再需要调用 "_start" 函数,只有需要执行 main 函数的时候才需要调用 "_start".

  • 【重大更新】test block 不再返回 Result 类型的结果。 现在的 test block 中使用错误处理机制对测试失败的情况进行处理,鼓励使用标准库中的 inspect 函数,和 @test 包中的 @test.eq 等辅助函数编写测试,比如:

test "unwrap should return value on Some" {
let some : Int? = Some(42)
inspect(some, content="Some(42)")!
@test.eq(some.unwrap(), 42)!
}
  • 支持使用 @pkg.C 来跨包使用构造器,比如如果在 @pkgA 中包含如下声明:
// pkgA
pub enum E1 { C1 } derive(Debug)

enum E1_hidden { C1 }

现在可以在另一个包中对 E1 的构造器 C1 直接进行使用,比如:

// pkgB
fn main {
debug(@pkgA.C1)
}

当在同一个包中,如果出现重复的 public constructor,则会报错,比如:

pub enum E1 {
C1
}

pub enum E2 {
C1
^^ ------ There can be at most one public constructor with name C1.
}

标准库更新

  • 迁移到新的错误处理机制。

  • 迁移到无符号整数,移除了Int和Int64类型旧的compare_udiv_umod_u函数;调整了下面的API:

    • Int32.trunc_double_uInt64.trunc_double_u 更改为 UInt.trunc_doubleUInt64.trunc_double

    • Int64::extend_i32_u 更改为 UInt64::extend_uint

    • Double::convert_i32_uDouble::convert_i64_u 更改为Double::convert_uintDouble::convert_uint64

构建系统更新

  • moon version --all 现在会显示 moonrun 的版本信息。
$ moon version --all
moon 0.1.20240705 (0e8c10e 2024-07-05) ~/.moon/bin/moon
moonc v0.1.20240705+7fdd4a042 ~/.moon/bin/moonc
moonrun 0.1.20240703 (52ecf2a 2024-07-03) ~/.moon/bin/moonrun
  • 修改moon new创建项目的license字段默认为空。

  • 默认开启诊断信息渲染 render

工具链更新

  • VSCode 插件安装MoonBit工具链功能调整。工具链安装的默认版本由总是安装最新版本调整为安装对应插件版本的工具链。

weekly 2024-07-01

· 阅读需 3 分钟

MoonBit 更新

  • 在没有歧义的情况下,enum构造器的使用可以省略类型前缀。比如现在可以直接使用 Some(42) 而不需要写 Option::Some(42);如果当前环境中有两个类型 T1T2 都定义了某个构造器 C,那么在使用的时候则需要通过上下文中的类型或者类型前缀指明是 T1::C 还是 T2::C,否则编译器会报错

  • 添加 UInt64 内建类型,支持加、减、乘、除、模以及 UInt64/Int64 的互相转换。

fn main {
let a = 0UL
let b : UInt64 = 1UL
println(a - b) //18446744073709551615
}
  • !! 后缀的语义修改为捕获函数调用中可能出现的错误,并返回 Result 类型,比如
fn f() -> Int!String { .. }
fn main {
let res = f()!! // res: Result[Int, String]
println(res)
}
  • moon test 中支持使用错误类型表示测试失败的情况,比如
fn eq[T : Debug + Eq](a : T, b : T, ~loc : SourceLoc = _) -> Unit!String {
if a != b {
let a = debug_string(a)
let b = debug_string(b)
raise ("FAILED: \(loc) `\(a) == \(b)`")
}
}

test "test_eq" {
eq(1+2, 3)!
}
  • 标准库中的 I/O 相关操作只保留了 println,其他操作将会在 io package 中提供

标准库更新

  • 统一 T::new()/T::make() 等创建容器对象的函数风格,移除了 T::with_capacity

  • 原先的 iteriteri 重命名为 eacheachiiter_reviter_revi 重命名为 each_reveachi_rev

  • as_iter 重命名为iter

构建系统更新

  • 预计将于本周开源

工具链更新

  • 优化调试体验,现在⽤户可在 JavaScript Debug Terminal 中执⾏ moon run --target js --debug 进行调试

  • moon info 和覆盖率工具适配错误类型和错误处理

weekly 2024-06-24

· 阅读需 2 分钟

MoonBit 更新

  • 支持了 32 位无符号整数
let num = 100U // 32位无符号整数的字面量需要后缀U
  • 在 wasm 后端导出返回值类型为 Unit 的函数时,之前导出函数的类型中会有 (result i32),现在 MoonBit 编译器会自动生成一个没有返回值 wrapper 函数,并将其进行导出

  • moonbitlang/core 的 API 一致性调整

    • forall/existall/any统一为all/any
    • containsmember 统一为contains

IDE 更新

  • 修复了 rename method 时候会丢失 type prefix 的 bug

  • 增加了 try ... catch ... 表达式中的 match clause 中补全的功能

构建系统更新

  • 增加了诊断信息渲染功能,目前还在试验阶段。可通过设置环境变量 MOON_RENDR=1 启用

diagnosis.png

  • moon bench 命令改为 moon generate-build-matrix,bench 子命令留待后续使用

weekly 2024-06-17

· 阅读需 3 分钟

MoonBit 更新

  • 支持了错误处理机制
  1. 函数返回值类型可以用 Int!String 来标识这个函数正常情况下返回 Int ,错误情况下会抛出类型为 String 的错误值,比如
fn div(x: Int, y: Int) -> Int!String { .. }
  1. raise 关键字用于中断当前控制流,直接抛出错误,比如
fn div(x: Int, y: Int) -> Int!String {
if y == 0 { raise "divide by 0" }
x / y
}
  1. try { expr0 } catch { pattern1 => expr1; pattern2 => expr2; .. } 表达式可以用于捕获 expr0 中抛出的错误,并对其进行模式匹配来处理,比如下面这个函数调用上面的 div 函数,并在 div 函数抛出错误的时候将错误信息打印,并返回默认值
fn div_with_default(x: Int, y: Int, default: Int) -> Int {
try {
div(x, y)!
} catch {
s => { println(s); default }
}
}
  1. 此外,可以用后缀运算符 !!! 进行错误处理,这些后缀运算符只能应用于函数调用,其中: f(x)! 将调用 f 的过程中发生的错误立即重新抛出。 f(x)!! 则会在 f 发生错误的情况下直接 panic,其等价于
try { f(x)! } catch { _ => panic() }

函数调用的形式包括方法调用,中缀运算符和管道运算符的调用,比如

fn init {
let _ = x.f()!!
let _ = (x + y)!!
let _ = (x |> f)!!
}
  1. 最后,对可能会抛出错误的函数如果没有使用上述任何错误处理,那么则会报 unhandled error 的错误
  • 支持 Map 字面量语法:
fn init {
// 键必须是字面量
let m1 : Map[String, Int] = { "x": 1, "y": 2 }
let m2 : Map[Int, String] = { 1: "x", 2: "y" }
}

IDE更新

  • 修复了 IDE 在补全过程中 builtin package 中的方法会重复出现两次的 bug

  • 修复了 IDE 中缺少 Byte 相关的补全功能

构建系统更新

  • 添加对 internal 包的支持,这些包被放在名为 internal 的目录中。internal 包只能被以 internal 的父目录为根的包导入。 例如,如果有一个包的路径为 username/hello/x/internal/a,该 internal 包的父目录为 username/hello/x,那么只有包username/hello/x 或其子包(例如 username/hello/x/a)能够导入username/hello/x/internal/a,而username/hello/y则不能导入该包。

weekly 2024-06-11

· 阅读需 2 分钟

MoonBit更新

  • 【Wasm MVP】Wasm1 后端添加基于 Perceus 算法的引用计数支持

  • 【语法】throw raise try catch 均被保留为关键字

    • 为了即将添加的错误处理机制
  • 【Core】Listsorted_map被移动至core/immut

    • List被移动至core/immut/list包中,并被移除内置类型支持

    let a = @immut/list.List::Cons(1, Cons(2, Nil))


    • sorted_map被移动至core/immut/sorted_map包中
  • 【Core】JSON API被优化,以提高性能并适应新的Core API

    • 新的类型定义
// Types and methods
pub enum JsonValue {
Null
// 原为Boolean(Bool)
True
False
Number(Double)
String(String)
Array(Array[JsonValue])
Object(Map[String, JsonValue]) // 原为@map.Map
}
  • 【JS】Int64性能优化
    • 在 JS 后端上,Int64现编译到两个Int,解决原先编译到BigInt的性能问题。同时,JS 下Int64的 runtime 实现现移动到 core 标准库中,便于开源社区参与 review 和改进。

构建系统更新

  • moon.mod.json 和 moon.pkg.json 在开发过程中支持注释,但是在 publish 时不允许注释(只支持标准 JSON 格式)

IDE更新

  • 【LSP】函数补全增加参数名称显示 LSP.png

weekly 2024-06-03

· 阅读需 3 分钟

MoonBit更新

  • 类型标注增加了新的语法T? 来表示Option[T]
struct Cell[T] {
val: T
next: Cell[T]?
}

fn f(x : Cell[T]?) -> Unit { ... }

相当于

struct Cell[T] {
val: T
next: Option[Cell[T]]
}

fn f(x : Option[Cell[T]]) -> Unit { ... }

旧的Option[T]仍然兼容,但是推荐使用更简短的新语法。moonfmt也会将Option[T]格式化为T?

  • 核心库 API 整理工作继续进行
    • Iter包被合入了builtin包。现在使用Iter[T]不需要@iter.前缀
pub fn any[T](xs : Iter[T], predicate : (T) -> Bool) -> Bool {
// ^不需要@iter.
match xs.find_first(predicate) {
None => false
Some(_) => true
}
}
  • Stack包被移入moonbitlang/x
  • 移除了List包,以及各类数据结构的to_listfrom_list函数。对于数据结构间转换和中间的表示结构,推荐使用Iter[T]Array[T]
  • 性能提升
    • 编译器现在会在分离编译的阶段进行一部分的闭包转化,从而改进了编译性能,并且对 JavaScript 后端生成的代码在特定情况下也进行了闭包转化
    • Option[Bool], Option[Char], Option[Byte], Option[Unit] 这些类型使用32位整数表示,其中 None 对应的值为 -1, Some(x) 对应的值为 x; Option[Int] 类型在 wasm 后端使用 64 位整数表示,其中 None 对应的值为 0x1_0000_0000, Some(x) 对应的值为 x, Option[Int]在 JavaScript 后端使用 int | undefined 表示,其中 undefined 表示 None
  • abort行为变更
    • 为了解除Wasm程序对于非标准的spectest.print_char的依赖,正在重构错误输出功能。
    • abort将不会利用spectest.print_char打印错误信息,行为与panic相同,等待功能进一步完善。

插件更新

  • 【语言服务器】修复了内存泄露的问题

weekly 2024-05-27

· 阅读需 3 分钟

MoonBit 周报 Vol.42:核心库进行API整理工作、工具链持续完善

MoonBit更新

  • 【核心库 Breaking】核心库进行API整理工作
    • 所有immutable数据结构被放在immut路径下,如@immutable_hashmap.Map变为@immut/hashmap.Map
// Before
let a : @immutable_hashmap.Map[Int, Int] = @immutable_hashmap.make()
// After
let a : @immut/hashmap.Map[Int, Int] = @immut/hashmap.make()
  • 核心库中 Option[T] 类型性能优化
    • 在类型 T 是一个引用类型情况下,对于 Option[T] 类型的值,Some(v) 会被直接编译成 vNone 会在 wasm-gc 后端被编译成 ref.null ,在 JavaScript 后端被编译成 undefined,从而避免内存分配
  • 核心库中引入了 fn panic[T]() -> T 函数,这个函数可以用于在测试块中,其中测试的名字需要以 "panic" 开头:
test "panic test ok" {
panic() // 测试通过
}

test "panic test failed" {
() // 测试失败
}

IDE更新

  • 【VS Code插件】增加了testfor的代码片段 test片段 test.gif for片段 for.gif

构建系统更新

  • 【初始化】moon new 会自动对创建的项目进行版本控制初始化,目前支持 git
  • 【测试】现在可以指定对多个包进行测试
moon test -p a b c
moon test -p a -p b -p c

工具链更新

  • 【安装】现在可以指定版本号进行安装
# Mac与Linux用户
# 下载最新版
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
# 下载 bleeding 版本
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s bleeding
# 下载特定版本
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s 0.1.20240520+b1f30d5e1
# Windows用户
# 下载最新版
irm cli.moonbitlang.cn/install/powershell.ps1 | iex
# 下载特定版本
$env:MOONBIT_INSTALL_VERSION = "0.1.20240520+b1f30d5e1"; irm cli.moonbitlang.cn/install/powershell.ps1 | iex

weekly 2024-05-20

· 阅读需 3 分钟

MoonBit更新

  • 【Breaking Change】Array重命名为FixedArray@vec.Vec重命名为Array
// Before
fn init {
let array : @vec.Vec[Int] = [1, 2, 3]
}
// After
fn main {
let array : Array[Int] = [1, 2, 3]
}
  • 【语法】键值对(如Map HashMap等)增加模式匹配支持
    • 类型需实现op_get方法,其键为原生类型(Int Char String Bool等),值为Option[T]
    • 匹配时,键需为字面量
    • { "key": pat } 中,模式 pat 类型是 Option[T]None表示 "key"不存在,Some(p)表示 "key"存在,且 p 会被用于匹配这个键的值
    • 匹配键值对的模式都是开放的:未被匹配的键即使存在也会被忽略掉
    • 键值对模式会生成优化过的代码,每个键至多被查询一次
fn main {
let map = @map.Map::[ ("a", 1) ]
match map {
// 当 `map` 包含 "b" 时匹配,
// 并把 "b" 在 `map` 中的值绑定到 `y`
{ "b": Some(y) } => println(y)
// 当 `map` 不包含 "b" 而包含 "a" 时匹配,
// 并把 "a" 的值绑定到 `k`
{ "b": None, "a": Some(k) } => println(k)
// 编译器提示 { "b": None, "a": None } 的情况未被匹配到
}
// 输出:1
}
  • 【语法】允许在已知类型信息的情况下省略newtype构造器
type A Int

pub fn op_add(self : A, other : A) -> A {
self.0 + other.0 // 省略构造器
}

fn main {
A::A(0) + 1 |> ignore // 省略 1 的构造器
let _c : A = 0 + 1 + 2
}

构建系统更新

  • 配置文件选项统一为kebab-case(近期仍对snake_case保持兼容)
{
"is-main": true,
"test-import": []
}
  • 【Wasm,Wasm-GC】后端支持在moon.pkg.json中指定导出内存名称(默认为moonbit.memory)与编译选项(如-no-block-params以兼容binaryen工具链)
{ 
"link": {
"wasm": {
"export-memory-name": "custom_memory_name",
"flags": ["-no-block-params"]
},
}
  • moon check 增加 --deny-warn 选项,在有 warning 时视为失败,返回非0值
  • moon fmt 增加 --check选项,用于检查当前代码是否已被格式化

标准库更新

  • 增加实验性库moonbitlang/x,用于开发与测试API不稳定的包。moonbitlang/x中的包稳定后,我们会根据社区的意见,选取重要的包合入moonbitlang/core
    • num time uuid json5 均已移动至moonbitlang/x
  • Bytes API 变更,从Int迁移到了Byte类型:
fn Bytes::op_get(self : Bytes, index : Int) -> Byte
fn Bytes::op_set(self : Bytes, index : Int, value : Byte) -> Unit
fn Bytes::length(self : Bytes) -> Int
fn Bytes::make(len : Int, ~init : Byte = b'\x00') -> Bytes

weekly 2024-05-13

· 阅读需 4 分钟

MoonBit更新

  • 支持了构造器的 payload 中出现 mutable field,使用方式如下:
enum E {
C(mut ~x : Int, mut ~y : Int)
} derive(Debug)


fn swap_xy(x : E) -> Unit {
match x {
// `~y` 会绑定模式匹配前 `C` 中的 `y` 字段的值
// 当看到 `C(..) as c` 这种模式时,编译器知道 `c` 一定是构造器 `C`,
// 所以可以在分支内部用 `c.x`、`c.y` 直接访问 `C` 的字段
C(~y, ..) as c => {
// `c.x` 和 `c.y` 可以用于修改/读取 `C` 中最新的值
c.y = c.x
c.x = y
}
}
}

fn init {
let e : E = C(x=1, y=2)
debug(e) // C(x=1, y=2)
swap_xy(e)
debug(e) // C(x=2, y=1)
}

这个例子中的 swap_xy 函数对构造器 Cxy 两个 field 的值进行了交换,并且这个交换是 in-place 的,没有引入额外的内存分配

  • **数组字面量默认构建 vector。**对 Array literal 语法进行了重载,现在 Array literal 可以用来构造 Vector 或者 Array,具体的类型通过上下文决定,当上下文中类型信息不明确时,默认使用 Vector,比如
fn init {
let v = [1, 2, 3] // v has type @vec.Vec[Int]
let a : Array[_] = [1, 2, 3] // a has type Array[Int]
}
  • 报错信息中添加了错误代码,例如
./vec/sort.mbt:68:16-68:23 [E4021] The value identifier minimum is unbound.
./vec/sort_by.mbt:90:16-90:23 [E4021] The value identifier minimum is unbound.
./vec/vec.mbt:962:37-962:50 [E4020] Package "iter" not found in the loaded packages.
./vec/vec.mbt:963:3-963:13 [E4024] The type/trait @iter.Iter is not found.

IDE更新

  • 对构造器中的 labeled field 的 label 支持了 gotodef/gotoref/rename 等功能,例如 label.PNG

构建系统更新

  • 支持在 package 级别配置 warn list 与alert list
    • moon.pkg.json 中如下配置,可在编译时关闭对应的 warn 与 alter【其中 2alter_1alert_2 分别为编译器预设的 warn id (对于 Unused variable) 与用户自定义的 alert id】
{
"warn_list": "-2",
"alert_list": "-alert_1-alert_2"
}
  • -代表关闭对应 id 的 warn 与 alter,可用 moonc build-package -warn-help 查看预设的 warn

  • moon check|build|run|test 的默认后端由wasm切换为wasm-gc

  • moon test 默认执行模式由 release 修改为 debug

  • moon check|build|run|test支持自动安装依赖,而无需手动执行 moon install

  • moon doc --serve 支持指定地址与端口

-b, --bind <BIND> [default: 127.0.0.1]
-p, --port <PORT> [default: 3000]
  • 优化 moon 体积
平台优化前优化后
macOS arm647.3 MiB3.6 MiB
macOS x86_648.2 MiB4.1 MiB
Ubuntu x86_6414.0 MiB9.6 MiB
Windows x86_649.4 MiB4.9 MiB

工具链更新

  • moonrun 支持打印 backtrace, 使用样例: 使用 moon new hello 创建一个新项目,将 main/main.mbt 的内容替换为:
  fn bar() -> Unit {
abort("")
}

fn foo() -> Unit {
bar()
}

fn main {
foo()
}

执行 moon run main --debug,可观察到输出类似:

error: RuntimeError: unreachable
at $username/hello/main.bar.fn/1 (wasm://wasm/6fe99e5e:wasm-function[3]:0xe6)
at $username/hello/main.foo.fn/2 (wasm://wasm/6fe99e5e:wasm-function[4]:0xea)
at *init*/3 (wasm://wasm/6fe99e5e:wasm-function[5]:0xef)
at <anonymous>:9:22