dram.me

Mercury的IO系统

之前我觉得Mercury的IO系统在实现上可能类似于Haskell,要全然掌握门槛较高,想着待对语言有一定了解后再看,没有深究。但最近有两个示例让我其产生了好奇。

一是Peter Wang在Mercury Wiki编写的教程里,有一个练习:如果更换包含状态变量的语句顺序,会有怎样的结果?例如:

main(IO0, IO) :-
       write_string("dear ", IO1, IO2),
       write_string("Hello, ", IO0, IO1),
       write_string("world!\n", IO2, IO).

输出的结果和正常的顺序相同,没有变化:

Hello, dear world!

由此可以猜测Mercury会基于状态变量的实例化(instantiation)情况重新组织语句。

再一个例子是Mercury自带的C接口调用的简单示例。其中对io::diio::uo变量没有太多操作,和我以前猜测的复杂数据结构封装相去甚远。

Prolog中的term expansion

SWI-Prolog Web开发教程有用到http_dispatch:http_handler,从文档中可知该指令使用了term expansion特性。

但有个奇怪的问题,对于以下代码:

:- use_module(library(http/http_dispatch)).
:- use_module(library(http/thread_httpd)).

% :- http_handler(/, say_hi, []).
:- http_dispatch:http_handler(/, user:say_hi, []).

say_hi(_Request) :-
    format('Content-type: text/plain~n~n'),
    format('Hello World!~n').

main :- http_server(http_dispatch, [port(8000)]).

如果在调用http_handler加了http_dispatch前缀,那么之后的say_hi/1也会被追加http_dispatch前缀。

具体可以查看Paul Moura的讨论

Swift获取随机数

补遗

  1. 可以直接利用Foundation中的FileHandle读取文件,不需要用Glibc。—— 2017-05-18

数值类型安全中一文中有将Swift获取随机数作为示例呈现,以下代码基于/dev/urandom实现:

import func Glibc.close
import func Glibc.open
import func Glibc.read
import var Glibc.O_RDONLY

let count = 5

let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: count)

let fd = open("/dev/urandom", O_RDONLY)
read(fd, buffer, count)
close(fd)

for i in 0..<count {
    print(buffer[i])
}

buffer.deallocate(capacity: count)

Swift的Glibc模块

补遗

  1. Swift强大的C接口能力的另一个例子是Swift Package Manager中对接系统库的示例。—— 2017-05-16

Swift语言在整体的设计基调上相对传统,但也不乏创新的点。这里要来说说的是其对C库的支持。

Swift包括一个Glibc模块,是对Glibc API的封装,而Swift的创新点在于利用clang实现了C头文件的解析和封装,这样接口封装这样繁重的工作可以自动化地完成。

以下通过Swift中一小段代码来感受下Glibc的便利性,代码摘取自benchmark/utils/DriverUtils.swift

import Glibc

typealias TimeT = timespec
func getTime() -> TimeT {
  var ticks = timespec(tv_sec: 0, tv_nsec: 0)
  clock_gettime(CLOCK_REALTIME, &ticks)
  return ticks
}

其中timespecclock_gettimeCLOCK_REALTIME都是C库原生定义的名称,不需要任何修改就可以直接在Swift代码中使用。另外对于ticks的初始化,是对timespec结构体的实例化,较C代码更为简洁。

下面再给出一个直接调用time获取时间的示例代码:

import Glibc

var now = time_t()
time(&now)
print(now)

或者:

import Glibc
var now = time(nil)
print(now)

数值类型安全

Swift语言有比较严格的类型安全检查,由此可以暴露出一些较难发现的问题。

例如,随机数种子的初始化动作,在C语言中,可以这样处理:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
        srandom(time(NULL));

        printf("%ld\n", random());

        return 0;
}

但在64位Linux系统中,time()的返回值和srandom()接收的参数类型是不一致的,一个是time_t,是64位符号整数,另一个是unsigned int,是32位无符号整数。但C语言并不会对此报错。

在Swift中,以下代码会报错:

import Glibc
srandom(UInt32(time(nil)))
print(random())

错误信息如下(64位系统中,Swift的Int为64位,C的int则依具体实现而定):

% swift foo.swift
foo.swift:2:9: error: cannot convert value of type 'time_t' (aka 'Int') to expected argument type 'UInt32'
srandom(time(nil))
        ^~~~~~~~~
        UInt32(  )

可以做类型转化,例如srandom(UInt32(time(nil)))。但这样依然存在问题,如果time()返回值大于UInt32.max,在类型转化时会有溢出错误。

因此,可以调整为srandom(UInt32(time(nil) % Int(UInt32.max))),或者还有一个更优的方式:srandom(UInt32(truncatingBitPattern: time(nil)))

← older posts