← 返回信息流
AI 资讯Hacker News·3 天前

hica 引入函数式编程方式

原标题:Functional Programming in hica

速览

文章讲解了 hica 这一技术框架/语言中函数式编程的核心概念与实际应用,包括高阶函数、不可变数据等特性。通过示例说明如何用函数式风格编写更简洁、可维护的代码。对希望提升编程范式理解的开发者有一定参考价值。

AI 深度解读

背景

函数式编程(Functional Programming, FP)是一种通过组合函数来构建程序的编程范式,与传统的指令式编程(通过改变状态的指令序列)形成对比。近年来,随着并发、数据密集型应用的增长,函数式编程因其不可变性、无副作用和易于推理的特性重新受到关注。hica 是一门围绕函数式风格设计的新语言,它借鉴了 Koka 的类型系统,强调表达式、不可变数据、高阶函数和纯函数。原文来自 Hacker News,作者通过大量可运行的示例,系统性地介绍了 hica 中函数式编程的核心概念,旨在帮助即使没有 FP 背景的读者快速上手。

核心内容

hica 的函数式编程设计围绕几个核心原则展开:一切皆表达式、默认不可变、纯函数默认、函数作为一等公民,以及强大的组合工具(如 map/filter/fold、flat_map、管道运算符等)。以下是对原文要义的完整翻译和梳理。

表达式,而非语句

在多数语言中,语句执行动作,表达式产生值。在 hica 中,几乎一切(包括 ifmatch 和代码块 {})都是表达式。这意味着可以在任何期望值的位置使用它们。例如:

fun sign(x) => if x < 0 { "negative" } else { "non-negative" }

{} 块体的值是其中最后一个表达式的值,无需 return 关键字:

fun clamp(x, lo, hi) {
  if x < lo { lo }
  else if x > hi { hi }
  else { x }
}

这条规则贯穿大部分 FP:当一切都有值时,一切都可以组合。

默认不可变

FP 避免共享可变状态。创建值后不能修改它,函数就更容易推理和测试。hica 使用 let 进行不可变绑定:

let name = "Alicia"
let scores = [85, 92, 78]

不能原地修改 scores,而是创建新列表:

let updated = scores + [95]      // 新列表: [85, 92, 78, 95]
let doubled = map(scores, (x) => x * 2)

当确实需要可变性(如计数器、循环变量)时,使用 var——它局部作用域并且不能泄漏到函数外:

var total = 0
for x in scores {
  total = total + x
}

var 是自愿选择(opt-in)的,其余一切保持不可变。

纯函数

纯函数对相同的输入总是返回相同的输出,并且没有副作用(无打印、无 I/O、无可变状态)。纯函数易于测试和组合。例如:

fun add(a, b) => a + b
fun square(x) => x * x
fun to_celsius(f: float) => (f - 32.0) * 5.0 / 9.0

在 hica 中,函数默认是纯函数。类型系统(继承自 Koka)会追踪 I/O 等效果,因此当函数具有副作用时,其类型中会体现出来。纯函数是你构建程序的基础,其余都是组合。

函数作为一等值

在 FP 中,函数如同整数、字符串一样是值。可以将它们存储在变量中、传递给其他函数,或从函数中返回。

fun apply(f, x: int) => f(x)

fun main() {
  let double = (x) => x * 2
  let greet = (name) => "Hello, " + name
  println(apply(double, 5))   // 10
  println(greet("Olle"))      // Hello, Olle
}

接受或返回另一个函数的函数称为高阶函数,apply 就是其中之一。

闭包

闭包是捕获其周围作用域变量的函数:

fun make_adder(n) => (x) => x + n

fun main() {
  let add5 = make_adder(5)
  let add10 = make_adder(10)
  println(add5(3))   // 8
  println(add10(3))  // 13
}

make_adder 每次返回一个新函数。该函数“闭合(closes over)”了 n,意味着即使在 make_adder 返回后,它仍会记住创建时的 n 值。这种模式可以按需定制专用函数:

fun make_multiplier(factor) => (x) => x * factor

fun main() {
  let triple = make_multiplier(3)
  let nums = [1..5]
  println(map(nums, triple))   // [3, 6, 9, 12, 15]
}

map、filter、fold

这三个函数覆盖了处理列表的大部分需求。

  • map:变换每个元素。接受一个列表和一个函数,将函数应用到每个元素,返回等长的新列表。

    fun main() {
      let nums = [1..5]
      println(map(nums, (x) => x * x))  // [1, 4, 9, 16, 25]
      println(map(nums, show))          // ["1", "2", "3", "4", "5"]
    }
    
  • filter:保留匹配的元素。仅保留谓词返回 true 的元素。

    fun main() {
      let nums = [1..8]
      let evens = filter(nums, (x) => x % 2 == 0)
      println(evens)   // [2, 4, 6, 8]
    }
    
  • fold(归约):将列表缩减为单个值。需要一个初始值和一个函数(累加器 acc + 当前元素 x)。

    fun main() {
      let nums = [1..5]
      let total = fold(nums, 0, (acc, x) => acc + x)
      println(total)     // 15
      let product = fold(nums, 1, (acc, x) => acc * x)
      println(product)   // 120
    }
    

    可以用 fold 实现许多列表操作,如 my_lengthmy_maxmy_reverse

flatten 与 flat_map

map 变换每个元素,但如果传给 map 的函数本身返回一个列表,结果就会变成列表的列表,通常不是想要的结果。例如将句子拆分成单词:

fun main() {
  let sentences = ["hello world", "foo bar", "one two three"]
  let split_words = map(sentences, (s) => split(s, " "))
  println(split_words)   // [["hello", "world"], ["foo", "bar"], ["one", "two", "three"]]
}

想要的是所有单词的扁平列表。两个函数解决这个问题:

  • concat:将一层嵌套展平。concat 接受列表的列表,折叠一层(其他语言中常称为 flatten)。

    let nested = [[1, 2], [3, 4], [5, 6]]
    println(concat(nested))   // [1, 2, 3, 4, 5, 6]
    
  • flat_map:一步完成 map 和 flatten。它应用函数到每个元素,然后连接所有结果列表。

    fun main() {
      let sentences = ["hello world", "foo bar", "one two three"]
      let all_words = flat_map(sentences, (s) => split(s, " "))
      println(all_words)   // ["hello", "world", "foo", "bar", "one", "two", "three"]
    }
    

当所映射的函数返回列表时,就该使用 flat_map

在管道中展开元素

flat_map 自然地融入管道。在某个步骤中一个元素变成多个元素时使用它:

fun expand(n) => [n, n * 10]          // 每个元素扩散为两个

fun main() {
  let result = [1, 2, 3]
    |> flat_map(expand)               // [1, 10, 2, 20, 3, 30]
    |> filter((x) => x >
查看原文 →hica.dev