惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
IT之家
IT之家
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
博客园 - 【当耐特】
The Cloudflare Blog
宝玉的分享
宝玉的分享
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Visual Studio Blog
小众软件
小众软件
博客园_首页
Last Week in AI
Last Week in AI
J
Java Code Geeks
V
V2EX
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
腾讯CDC
博客园 - 司徒正美
Engineering at Meta
Engineering at Meta
The GitHub Blog
The GitHub Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
The Register - Security
The Register - Security
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
N
Netflix TechBlog - Medium
F
Full Disclosure
B
Blog
H
Help Net Security
C
Check Point Blog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
L
LangChain Blog
P
Proofpoint News Feed
D
Docker
Microsoft Security Blog
Microsoft Security Blog

szhshp 的第三边境研究所

假设, AI 把我代替的那一刻真的到来 | szhshp 的第三边境研究所 小傻瓜都能懂的 AstrBot QQ 机器人集成 MCP 功能实战指南 | szhshp 的第三边境研究所 《智人之上: 从石器时代到 AI 时代的信息网络简史》阅读笔记 | szhshp 的第三边境研究所 iPadOS 26 无法设置空间场景图片壁纸的解决方法 | szhshp 的第三边境研究所 《一路云海》(终) | szhshp 的第三边境研究所 《一路云海》(四): 如何不按套路旅行 | szhshp 的第三边境研究所 《一路云海》(三): In Ya Mellow Tone | szhshp 的第三边境研究所 《一路云海》(二): 关西世博参观纪实 | szhshp 的第三边境研究所 《一路云海》(一): 新的征程 | szhshp 的第三边境研究所 2025 大阪世博会 [ 3 天前-先到先得 ] 阶段 场馆预约必中独家攻略 | szhshp 的第三边境研究所 Hackathon 随想 | szhshp 的第三边境研究所 一杯双皮奶 | szhshp 的第三边境研究所 Armbian + CasaOS + NAS 配置指南 | szhshp 的第三边境研究所 Docker 构建镜像报错: error getting credentials - err: exit status 1, out: `` | szhshp 的第三边境研究所 Disqus RIP! 论过高的维护成本如何治疗固执的坏习惯 | szhshp 的第三边境研究所 炸弹猫桌游变体规则 | szhshp 的第三边境研究所 《小岛经济学》阅读笔记 | szhshp 的第三边境研究所 《金钱心理学》阅读笔记 | szhshp 的第三边境研究所 为知笔记 RIP: 迁移剩余的笔记 | szhshp 的第三边境研究所 2025 博客第十年展望 - 再见我的过去 | szhshp 的第三边境研究所 我在独立游戏里面致敬的作品 | szhshp 的第三边境研究所 《How to make thing faster》阅读笔记 | szhshp 的第三边境研究所 《The Art of Clean Code》阅读笔记 | szhshp 的第三边境研究所 《Clean Architecture: A Craftsman Guide to Software Structure and Design》阅读笔记 | szhshp 的第三边境研究所 《How AI Works》阅读笔记 | szhshp 的第三边境研究所 游戏策划废案 - Project Uranus | szhshp 的第三边境研究所 游戏策划废案 - Project X | szhshp 的第三边境研究所 人生第一款独立游戏开发复盘 | szhshp 的第三边境研究所 Trap of Life | szhshp 的第三边境研究所 如果我用手搓了个暗物质雏形 | szhshp 的第三边境研究所 《Rust Programming Language》- EX - Cheatsheet | szhshp 的第三边境研究所 新的播放器上线了 | szhshp 的第三边境研究所 Cypress - End-to-End Testing: OKTA Authentication with MFA | szhshp 的第三边境研究所 VSC x VIM - 反正多学几个快捷键没有坏处 | szhshp 的第三边境研究所 两小时 Docker 速成 - Getting Started | szhshp 的第三边境研究所 《The Joy of Javascript》- 5 - Data | szhshp 的第三边境研究所 《The Joy of Javascript》- 4 - Meta Programming | szhshp 的第三边境研究所 Web-Automation Testing | szhshp 的第三边境研究所 《The Joy of Javascript》- 2 - Functor/Monad | szhshp 的第三边境研究所 《The Joy of Javascript》- 1 - Object/Function | szhshp 的第三边境研究所 EvalError: Possible side-effect in debug-evaluate | szhshp 的第三边境研究所 前端 QA 工具链指南 - husky,commitlint,commitizen,eslint,typescript,express | szhshp 的第三边境研究所 效率工具 & 软件推荐 | szhshp 的第三边境研究所 站点开源 Project Titan | szhshp 的第三边境研究所 cmd/powershell-命令速查-Cheatsheet | szhshp 的第三边境研究所 Git-命令速查与相关问题解决-Cheatsheet & Troubleshooting | szhshp 的第三边境研究所 Tailwind.css not included in NextJS Production | szhshp 的第三边境研究所 Github GraphQL API - Data Integration | szhshp 的第三边境研究所 尝试 Windows Terminal + z.lua | szhshp 的第三边境研究所 StrokePlus.net - Powerful Hotkey Boost | szhshp 的第三边境研究所 优雅地 Hack 一台黑苹果 | szhshp 的第三边境研究所 一杯关东煮 | szhshp 的第三边境研究所 Gitee x PicGo 管理图床 | szhshp 的第三边境研究所 Create Your Own Redux (Typescript) | szhshp 的第三边境研究所 Getting Started: Ionic + Capacitor + React + Typescript + Android | szhshp 的第三边境研究所 GraphQL: File Upload & Troubleshooting | szhshp 的第三边境研究所 吉祥航空随心飞刷票工具循环查票工具 | szhshp 的第三边境研究所 NVM Windows Portable Installation | szhshp 的第三边境研究所 NextJS x Typescript - Integration & Troubleshooting | szhshp 的第三边境研究所 全站点升级 - Project Titan | szhshp 的第三边境研究所 AWS-EC2-常见问题解决-Troubleshooting | szhshp 的第三边境研究所 Workbox-Getting Started | szhshp 的第三边境研究所 《时间简史》阅读笔记 | szhshp 的第三边境研究所 《时间的秩序》阅读笔记 | szhshp 的第三边境研究所 ESLint-常见问题解决-Troubleshooting | szhshp 的第三边境研究所 站点索引 | szhshp 的第三边境研究所 个人知识库搭建 - Hexo 实现 Github/Coding 持续集成 | szhshp 的第三边境研究所 BMOB-常见问题解决-Troubleshooting | szhshp 的第三边境研究所 React + Typescript: 开启 HMR/Hot Loader | szhshp 的第三边境研究所 Yarn & NPM | szhshp 的第三边境研究所 ESLint: Typescript + React + TSX 集成 | szhshp 的第三边境研究所 《10101》EP15:过年 2020 | szhshp 的第三边境研究所 Cmder-Usage | szhshp 的第三边境研究所 Redux-DOM not refreshed | szhshp 的第三边境研究所 React Semantic UI-CheatSheet | szhshp 的第三边境研究所 Typescript: Getting Started | szhshp 的第三边境研究所 Flutter-Getting Started | szhshp 的第三边境研究所 Typescript: Access window object with type check | szhshp 的第三边境研究所 VSC Extension Development-Create A Code Formatter Extension | szhshp 的第三边境研究所 OneNote Extension-Main API | szhshp 的第三边境研究所 TortoiseGit Win10 Icon not showing correctly | szhshp 的第三边境研究所 Mongoose: aggregate() 方法实现聚合函数 | szhshp 的第三边境研究所 React In Depth - React 源码解析 - render() & Fiber | szhshp 的第三边境研究所 MongoDB x Mongoose: 实现类似 Join 的功能 | szhshp 的第三边境研究所 Disqus 评论框改造工程-实现 Disqus 代理的 Serverless 服务 | szhshp 的第三边境研究所 GUI, CLI, Terminal, Shell, TTY, Console, Bash | szhshp 的第三边境研究所 Jest Async Best Practise: No Nested Async | szhshp 的第三边境研究所 关系型数据库与非关系型数据库优缺点分析 | szhshp 的第三边境研究所 New Edge & Chromium Error - Package is invalid: 'CRX_VERSION_NUMBER_INVALID' | szhshp 的第三边境研究所 选择 GraphQL 的 N 个理由 | szhshp 的第三边境研究所 StrokePlus - 鼠标手势神器的代码编写技巧 | szhshp 的第三边境研究所 嗨,好久不见 | szhshp 的第三边境研究所 Vue + ElementUI 集成 Vue Router | szhshp 的第三边境研究所 IE8 - Unknown Runtime Error Using innerHTML & Ajax | szhshp 的第三边境研究所 React: Handling Events | szhshp 的第三边境研究所 React-Navigation Integration with Redux | szhshp 的第三边境研究所 React: States is tricky | szhshp 的第三边境研究所 《10101》EP14:小剧场 | szhshp 的第三边境研究所 ColdFusion - Basic - arguments in cffunction | szhshp 的第三边境研究所 ColdFusion - Basic - cfset | szhshp 的第三边境研究所
《The Joy of Javascript》- 3 - ADT(Algebraic Data Type) | szhshp 的第三边境研究所
2022-07-06 · via szhshp 的第三边境研究所

Meta

目录

相关文章

一本书里面内容较多, 因此分成了多篇 Post, 可以从此处看到相关文章:

ADT | Algebraic data types

注意这里不是指 Abstract data types

  • ADT 也是一种 composite data structure
  • ADT 内可以包含不同类型的多种元素
  • ADT 也需要满足 identity, composability 几个要求

Kinds of ADT

主要提及 ADT 两种主要的形式: PairChoice

Pair

其实就是一种 AND 条件, 一个 ADT 中可以包含多个值

const compose2 = (f, g) => (...args) => f(g(...args))
const compose = (...fns) => fns.reduce(compose2)

const Pair = (left, right) =>
  compose(
    Object.seal,
    Object.freeze
  )({
    left,
    right,
    toString: () => `Pair [${left}, ${right}]`
  });


const p = Pair('a', 'b');
console.log(p.toString()); //Pair [a, b]

这里还有另一个例子:

class BoolTuple2 {
  constructor(a, b) {
    if (typeof a !== "boolean" || typeof b !== "boolean") {
      throw new Error("Bool2Tuple must have exactly two boolean values");
    }
    this.a = a;
    this.b = b;
    Object.freeze(this);
  }
}

/* 可轻易的发现 BoolTuple2 最多有四种可能性, 并且只有四种可能性. */
const tt = new BoolTuple2(true, true);
const tf = new BoolTuple2(true, false);
const ft = new BoolTuple2(false, true);
const ff = new BoolTuple2(false, false);

Choice

其实就是一种 OR 条件, 可以包含多个不同的值但是在同一种情况下只能用其中一个值.

class BoolOrDigit {
  constructor(val) {
    if (typeof val !== "boolean" && !(val instanceof Digit)) {
      throw new Error("BoolOrDigit can only hold either a boolean or digit");
    }
    if (typeof val === "boolean") {
      this.value = val;
    }
    if (val instanceof Digit) {
      this.value = val.value;
    }
    Object.freeze(this);
  }
}

ADT Example - Validation

首先实现一个 Validation 类:

class Validation {
  #val;
  constructor(value) {
    this.#val = value;
    /* 此处判断是要求必须使用 Validation.Of 进行 init */
    if (![Success.name, Failure.name].includes(new.target.name)) {
      throw new TypeError( 
        `Can't directly instantiate a Validation. Please use constructor Validation.of` 
      );
    }
  }
  get() {
    return this.#val;
  }
  static of(value) {
    return Validation.Success(value);
  }
  static Success(a) {
    return Success.of(a);
  }
  static Failure(error) {
    return Failure.of(error);
  }
  get isSuccess() {
    return false;
  }
  get isFailure() {
    return false;
  }
  getOrElse(defaultVal) {
    return this.isSuccess ? this.#val : defaultVal;
  }
  toString() {
    return `${this.constructor.name} (${this.#val})` ;
  }
}

然后扩展出两种情况:


class Success extends Validation {
  static of(a) {
    return new Success(a);
  }
  /* Override */
  get isSuccess() {
    return true;
  }
}


class Failure extends Validation {
  static of(b) {
    return new Failure(b);
  }
  get() {
    /* 对于报错的情况这里直接返回一个 Error */
    throw new TypeError( `Can't extract the value of a Failure` );
  }
  /* Override */
  get isFailure() {
    return true;
  }
}

最后在方法里面进行判断, 判断放在外部, 最终返回一个 Validation 类型:

const read = (f) =>
  fs.existsSync(f)
    ? Success.of(fs.readFileSync(f))
    : Failure.of( `File ${f} does not exist!` );

read('chain.txt'); // Success(<Buffer>) 
read('foo.txt'); // Failure('File foo.txt does not exist!')

如此一来成功和失败都会得到一个 Validation.

ADT + Functor

Success with Functor

将 Functor 扩展到 Success 里面, 使得返回的结果附带 map 方法:

Object.assign(Success.prototype, Functor);

const countBlocksInFile = (f) =>
  read(f)
    .map(decode("utf8"))
    .map(JSON.parse)
    .map(count);
    
countBlocksInFile("foo.txt"); // Success(<number>)

Failure with NoopFunctor

将 Functor 扩展到 Failure 里面, 但和之前不同的是这里的 map 单纯返回一个自身不做任何操作:

const NoopFunctor = {
  /* 如果验证失败的情况下, Functor 就提供一个什么都不做的 map 方法 */
  map() {
    return this;
  }, 
};

Object.assign( Failure.prototype, NoopFunctor);

const fromNullable = (value) =>
  value === null ? Failure.of("Expected non-null value") : Success.of(value);

fromNullable("joj").map(toUpper).toString(); // 'Success (JOJ)'

/* 下面这个方法一开始就验证失败, 因此后面的 map 方法其实什么都没有做 */
fromNullable(null).map(toUpper).toString(); // 'Failure (Expected non-null value)'

Usage

class Block {
  //... omitting for brevity
  isValid() {
    const { index: previousBlockIndex, timestamp: previousBlockTimestamp } =
      this.#blockchain.lookUp(this.previousHash);

    /* 下面三个方法全部都返回一个 Validation */
    const validateTimestamps = checkTimestamps(previousBlockTimestamp, this);
    const validateIndex = checkIndex(previousBlockIndex, this);
    const validateTampering = checkTampering(this);
    
    /* 最终通过 Validation 的 isSuccess 方法判断验证是否成功 */
    return (
      validateTimestamps.isSuccess &&
      validateIndex.isSuccess &&
      validateTampering.isSuccess
    );
  }
}


const ledger = new Blockchain();
let block = new Block(ledger.height() + 1, ledger.top.hash, ['some data']);

block = ledger.push(block);
block.isValid(); // true

block.data = ['data compromised']; 
block.isValid(); // false 

ADT + Monad


const Monad = {
  flatMap(f) {
    return this.map(f).get()
  },
  chain(f) {
    return this.flatMap(f)
  },
  bind(f) {
    return this.flatMap(f)
  }
}
Object.assign(Success.prototype, Monad);

const NoopMonad = {
  flatMap(f) {
    return this;
  },
  chain(f) {
    return this.flatMap(f);
  },
  bind(f) {
    return this.flatMap(f);
  }
}
Object.assign( Failure.prototype, NoopMonad );



class Block {
  /*  ... */
  isValid() {
    const {
      index: previousBlockIndex,
      timestamp: previousBlockTimestamp
    } = this.#blockchain.lookUp(this.previousHash);

    return Validation.of(this)
      .flatMap(checkTimestamps(previousBlockTimestamp))
      .flatMap(checkIndex(previousBlockIndex))
      .flatMap(checkTampering);
  }
}

Reference

Validation ADT in OLOO version

const Validation = {
  init: function (value) {
    this.isSuccess = false;
    this.isFailure = false;
    this.getOrElse = (defaultVal) => (this.isSuccess ? value : defaultVal);
    this.toString = () => `Validation (${value})`;
    this.get = () => value;
    return this;
  },
};
const Success = Object.create(Validation);
Success.of = function of(value) {
  this.init(value);
  this.isSuccess = true;
  this.toString = () => `Success (${value})`;
  return this;
};
const Failure = Object.create(Validation);
Failure.of = function of(errorMsg) {
  this.init(errorMsg);
  this.get = () => throw new TypeError(`Can't extract the value of a Failure`);
  this.toString = () => `Failure (${errorMsg})`;
  return this;
};

Summary

  • Wrapping bare data inside a mappable container provides good encapsulation and immutability.
  • JavaScript provides an object-like façade for its primitive data types that preserves their immutability.
  • Array#{flat, flatMap} are two new APIs that allow you to work with multidimensional arrays in a fluent, composable manner.
  • map and compose have a deep semantic equivalence.
  • An ADT can be classified by how many values it can support (record or choice) and by the level of composability (functor or monad).
  • Functors are objects that implement the map interface. Monads are objects that implement the flatMap interface. Both support a set of mathematically inspired protocols that must be followed.
  • Validation is a composite choice ADT modeling Success and Failure conditions.
  • Whereas compose is used for regular function composition, composeM is used for monadic, higher-kinded composition.
  • By implementing the universal protocols of Functor and Monad, you can integrate your code easily and seamlessly with third-party FP libraries.
  • Use the newly proposed pipeline operator to run sequences of monadic transformations in a fluent manner.