The cool things of Gleam

The cool things of Gleam

Gleam 的酷炫之处

Ok. I am liking Gleam. I haven’t been working with the language for long (read, last week). But still. I am REALLY liking the simplicity. These are some quick notes from learning the language so far. 好吧,我开始喜欢上 Gleam 了。虽然我接触这门语言的时间并不长(也就是上周才开始),但我真的非常喜欢它的简洁性。以下是我在学习过程中的一些简要笔记。

The @deprecated attribute

@deprecated 属性

It is easy to miss how valuable. But if you been writing too much GraphQL, you will know the @deprecated decorator. In Gleam, @deprecated works the same way. You add a function attribute to tell the compiler where people should go for the new way: 你可能很容易忽略它的价值。但如果你写过很多 GraphQL,你一定很熟悉 @deprecated 装饰器。在 Gleam 中,@deprecated 的作用方式如出一辙。你可以添加一个函数属性,告诉编译器用户应该去哪里寻找新的替代方案:

@deprecated("Use new_way. It's better") 
fn old_way(x: String) -> String { x } 

fn new_way(x: nice) -> nice { x }

Simple and immensely effective. Helps provide a path to where to look for new versions of a library function. Once you have such an annotation system, developing gets a little bit easier. And you miss it quite quickly when you don’t have it! 简单且极其有效。它为寻找库函数的新版本提供了指引。一旦你拥有了这样的注解系统,开发工作就会变得轻松一些。而当你失去它时,你会很快感到不习惯!

Todo

Todo

Have you ever written somewhere in your code a todo? I mean… there’s literal todo linters out there. Well, in Gleam, todo is a base construct. You identify like so: 你是否曾在代码的某个地方写过 todo?我是说……市面上甚至有专门的 todo 代码检查工具。而在 Gleam 中,todo 是一个基础构造。你可以这样定义:

fn something() -> String { todo as "maybe, should do something" }

Allowing you to not only identify there is something to do, but with details on what the todo may actually need to be. I cannot tell you how many times I’ve written these little todo in comments. So let’s just say, I am content with the option being baked in. 这不仅让你能标记出有待完成的工作,还能提供关于该 todo 具体需要做什么的详细信息。我数不清自己曾在注释里写过多少次这种小小的 todo 了。所以,我只能说,我很满意这个功能被直接内置在语言中。

Generics

泛型

Anywhere you go, you will find papers upon papers over “correctness” for how to do Generics. Also known as parametric polymorphism. For example, in Go we had to wait for years until 1.18: 无论你走到哪里,都能找到大量关于如何实现“正确”泛型(也称为参数多态)的论文。例如,在 Go 语言中,我们等待了多年才等到 1.18 版本:

type Num interface { int | string } 
func Mistakes[T Num](val T) T { return val }

Go’s way isn’t bad. Especially when you remember how to do this in C++. Or again in Go where you still have to argue with others about using generics instead of interfaces… But in Gleam: Go 的方式并不坏。特别是当你回想起 C++ 是如何实现泛型的,或者在 Go 中你还得为了“该用泛型还是接口”而与他人争论时……但在 Gleam 中:

fn Mistakes(val: woah) -> woah { val }

Gleam handles the generic type as part of functional type annotation. Some powers are lost in more stronger construct. Like constrained generics. But, the benefit is simplicity and readability. Gleam 将泛型类型作为函数类型注解的一部分来处理。虽然在更强大的构造(如约束泛型)中会损失一些能力,但其带来的好处是简洁性和可读性。

case EVERYTHING

一切皆 case

You see, in BEAM languages like Elixir, Erlang, or Gleam, you will have to do pattern matching. It is a super power of these languages. Maybe the super power. For a simple example that translate outside of the beam, take checking if an integer is zero. In Elixir you can do this magic with functions: 你看,在像 Elixir、Erlang 或 Gleam 这样的 BEAM 语言中,你必须使用模式匹配。这是这些语言的超能力,甚至可以说是核心超能力。举一个在 BEAM 之外也能理解的简单例子:检查一个整数是否为零。在 Elixir 中,你可以通过函数实现这种魔法:

def zero?(0), do: true 
def zero?(maybe) when is_integer(maybe), do: false

Which translate to something in Go like the following: 这在 Go 中相当于:

func zero(maybe int) bool { 
    switch maybe { 
    case 0: return true 
    default: return false 
    } 
}

I personally really like Elixir’s magic. It feels like alchemy. But also… it kind of is. There is macros, atoms, guard constructs, functional type checks, overloads, and pattern matching to make those two lines work. 我个人非常喜欢 Elixir 的魔法,感觉就像炼金术一样。但说实话……它确实有点像。为了让那两行代码生效,背后涉及了宏、原子(atoms)、守卫构造、函数类型检查、重载和模式匹配。

In Gleam, the language takes the approach of being meticulous on doing things one simple way. Which means, case expressions are your everything: 而在 Gleam 中,这门语言采取了一种严谨的方式,即用一种简单的方法来处理事情。这意味着,case 表达式就是你的一切:

fn zero(maybe Int) -> Bool { 
    case maybe { 
        0 -> True 
        _ -> False 
    } 
}

While may seem similar to Go’s approach and less so of Elixir’s, Gleam is in checking what each an operational response should be. So constructs that are harder on non pattern matching languages, like deconstruction of lists, are still straight case matches: 虽然这看起来与 Go 的方式相似,而不像 Elixir,但 Gleam 的重点在于检查每个操作响应应该是什么。因此,在非模式匹配语言中较难处理的构造(如列表解构),在 Gleam 中依然只是简单的 case 匹配:

// return first list with at least 2 elements 
fn first_with_many(lists: List(List(t))) -> List(t) { 
    case lists { 
        [] -> [] 
        // notice the pattern alias! 
        [[_, _, ..] as first, ..] -> first 
        [_, ..rest] -> first_with_many(rest) 
    } 
}

No Exceptions!

没有异常!

For so many languages, exceptions is simply a way of dealing with errors. Exception handling in python is practically a requirement. It is inevitable. However in Gleam, the type system is built for no exceptions. Elm is notorious for this type of design. To do such a thing outside of such languages, you have to be absolutely foaming methodical on how you write code. But in Gleam, it is simply part of how the language works. 对于许多语言来说,异常仅仅是处理错误的一种方式。在 Python 中,异常处理几乎是必须的,它是不可避免的。然而在 Gleam 中,类型系统被设计为没有异常。Elm 以这种设计而闻名。要在其他语言中做到这一点,你必须在编写代码时极其严谨。但在 Gleam 中,这只是语言工作方式的一部分。

let x: Option(Int) = None 
// default to -1 if None 
let y = option.unwrap(x, -1)

You either get the result of unwrap, or you get a default you define. Done. 你要么得到 unwrap 的结果,要么得到你定义的默认值。搞定。

No Nulls

没有 Null

This one is one that is definitely coming from the Rust part of Gleam. The language doesn’t have null/nil values. Yes. Technically there is a Nil type, but it is only able to be set to itself. You can’t make an Int a Nil value. However, there is such a thing as an absence of something. You use Options for that. 这一点显然是受到了 Gleam 中 Rust 特性的影响。这门语言没有 null/nil 值。是的,技术上确实存在一个 Nil 类型,但它只能被赋值为它自己。你不能把一个 Int 变成 Nil 值。不过,确实存在“缺失”的概念,你可以使用 Options 来处理。

let answer = Some(42)

And if you want to know of errors, you use Results. 如果你想了解错误,可以使用 Results。

let err = Error("yes, things went the wrong way")

And that’s it! There’s other neat tricks on the language, but the simplicity is what really selling its abilities for me. 就是这样!这门语言还有其他巧妙的技巧,但简洁性才是我真正被它吸引的原因。