let's Code with Function
关于函数式编程:
函数式编程是有别于传统的面对对象范式的编程范式,函数式方向是目前编程语言发展的大方向,所有新设计的编程语言都或多或少的引入了函数式编程功能。
笔者认为,「下一代计算机学科体系」将基于函数式编程语言。因此,打好函数式编程基础对于具备「长期主义」思维的程序员是必要的。
关于本专栏:
本专栏将通过实战代码分析与经典著作解读,分享作者关于函数式编程与区块链的思考与实践。就目前而言,本专栏将基于两种函数式语言:Rust 和 Elixir,有时候会提及其它语言,作为辅助性参考。
关于太上:
太上是笔者团队近期实践的一个函数式+区块链的项目。
太上炼金炉在不改变原有 NFT 合约的基础上,通过附加「存证合约」,赋予 NFT 组合、拆解、生命周期、权益绑定等能力,锻造 NFT +,创造无限创新玩法与想象空间。
项目地址: https://github.com/WeLightProject/Tai-Shang
愿景0x01: 助力所有 NFT 及其相关项目,让其具备无限商业想象空间与无限玩法。
愿景0x02: 成为下一代区块链基础设施
太上是本系列用以探讨函数式编程的第一个项目。
什么是 Binary? Binary 中文为二进制类型,是一种各编程语言中都存在的基本数据类型,在部分语言(如Python)中,将其称之为 Bytes。
Binary 可以看做是 0-255 的整型构成列表(List),如:<<255,18,33>>
。
Binary 和字符串(String)是什么关系? 字符串是 Binary 的子集,字符串能转化为 Binary,但不是所有的 Binary 都能转化为String。
Binary 在区块链中有哪些应用?
在区块链中随处可见 Binary 的身影,如私钥(Privkey)、公钥(Pubkey)、地址(Address)均可以表现为
Binary 的形式;智能合约编码后是 Binary;签名(Signature)也是 Binary。
虽然在本质上等价,但是在不同语言中 Binary 会呈现不同的形式。
Python:
>> payload = bytes('你好 世界', encoding='UTF-8')
>> print(payload)
b'\xe4\xbd\xa0\xe5\xa5\xbd \xe4\xb8\x96\xe7\x95\x8c'
Java:
class BytesDemo
{
public static void main(String[] args)
{
byte payload[] = new byte[3];
payload[0] = (byte) 0x0A;
payload[1] = (byte) 0xFF;
payload[2] = (byte) 0x01;
for (byte theByte : payload){
System.out.println(Integer.toHexString(theByte));
}
}
}
NodeJs:
> var payload = new Buffer('hello world')
> Buffer.prototype.toByteArray = function () {return Array.prototype.slice.call(this, 0)}
> payload.toByteArray()
[ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
Rust:
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82"; // SHIFT-JIS 编码的 "ようこそ"
Elixir:
iex> <<11,22,33,44>>
万变不离其宗。我们要认识到,本质上来讲这些 Binary 都是一样的。
太上中的基因,是对于价值容器(NFT)承载的属性的一种抽象表达。具体渲染出来的实体,不管是渲染出一只猫还是一把屠龙宝刀,这事和基因本身是解耦的。
因此,笔者设计了这样的 Binary 模型:
Gene
<<33, 44, 66, 22, 11, 55, 234, 111>>
|---| |---------------------|-----
| |
前1/4处用以表示二值属性,如男/女 后3/4用以表示数值属性,如魔法值
在处理 Gene 的时候,函数式编程中的 「Pattern Match」(模式匹配)就表现出它的威力了,我们可以通过 Pattern Match 充分玩转 Binary。
下面将以基因分离为例,讲解「变量层面的模式匹配」这个知识点。
目标:实现函数,将基因的二值部分与其余部分分离。
Elixir 实现:
@spec split_gene(binary) :: {binary, binary}
def split_gene(gene) do
base2_size =
gene
|> byte_size()
|> div(4)
# 1/4 is base2
<<binary_base2::bytes-size(base2_size), binary_base10::binary>> = gene
{binary_base2, binary_base10}
end
只需一行代码,借助变量层面的模式匹配,我们便抽离出了binary_base2
与binary_base10
。
Rust:
Rust 中可借助nom
这个库,以下是解析 hex color 为 RGB 的例子:
fn hex_color(input: &str) -> IResult<&str, Color> {
let (input, _) = tag("#")(input)?;
let (input, (red, green, blue)) = tuple((hex_primary, hex_primary, hex_primary))(input)?;
Ok((input, Color { red, green, blue }))
}
常见地,「元」 Binary 可以被编码为 Base16、Base32、Base64 等多种形式。
Binary 编码有助于让信息的传输更有效率,因此,Base64编码在我们的日常开发中非常常见。
和区块链相关的还有一种很重要的Binary 编码形式:Base58,Base58 的改进版 Base58Check 被用于比特币的地址生成中。
base58和base64一样是一种二进制转可视字符串的算法,主要用来转换大整数值。区别是,转换出来的字符串,去除了几个看起来会产生歧义的字符,如 0 (零), O (大写字母O), I (大写的字母i) and l (小写的字母L) ,和几个影响双击选择的字符,如/, +。
——https://www.jianshu.com/p/e002931bb38b
比特币之所以加入改进版的 Base58 算法,主要为了解决 Base58 导出的字符串没有校验机制,这样,在传播过程中,如果漏写了几个字符,会检测不出来。所以使用了改进版的算法 Base58Check。
实现是:在encode前,在输入流尾部加入输入内容的hash值(4个字节)。然后再对输入流进行 Base58Encode。
在 decode 时候:先 Base58Decode, 然后拆成两部分(内容和校验值),判断对内容计算的校验值和校验值字段是否一致。
比特币Base58相关源码地址: https://github.com/bitcoin/bitcoin/blob/master/src/base58.cpp
在 Rust 中,我们通过base**库来实现 Binary 的编码:
use base64::{encode, decode};
......
let result = encode(payload);
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!