如何为单字符ASCII/UTF-8字符串编写Elixir Typespec?
Let's tackle your two questions step by step—Elixir's Typespec system has some limitations when it comes to binary bit pattern constraints, but there are workable solutions.
1. Typespec for a Single ASCII Character (0-127)
First, the straightforward truth: you can't directly express the "first bit is 0" constraint in a Typespec. Elixir's Typespec parser doesn't support binary types with specific bit value rules (like <<0::1, _::7>>).
That said, you can define a base type for single-byte binaries, then use runtime guards to enforce the ASCII range:
# Define the base type: any single-byte binary @type ascii_char :: <<_::8>> # Add a spec and guard to validate it's actually ASCII @spec validate_ascii(ascii_char) :: :ok | {:error, :non_ascii} def validate_ascii(char) when byte_size(char) == 1 and char <= <<127>> do :ok end def validate_ascii(_char) do {:error, :non_ascii} end
The <<_::8>> type ensures we're dealing with a single byte, and the guard char <= <<127>> checks that the byte value falls within the ASCII range (0-127).
2. Typespec for a Single UTF-8 Character
Your bit pattern union works perfectly for pattern matching, but Typespec can't parse those specific bit constraints (like 6::3 or 14::4). That's exactly why you're seeing that ArgumentError—the Typespec parser doesn't recognize that syntax.
Instead, here are two reliable approaches:
Option 1: Use String.t() with Runtime Checks
A single UTF-8 character in Elixir is just a string of length 1 (where length is measured in Unicode codepoints, not bytes). You can define the type and validate it with guards:
@type utf8_char :: String.t() @spec validate_utf8_char(utf8_char) :: :ok | {:error, :invalid_utf8_char} def validate_utf8_char(char) when is_binary(char) and String.length(char) == 1 and String.valid?(char) do :ok end def validate_utf8_char(_char) do {:error, :invalid_utf8_char} end
The String.valid?/1 check ensures the binary is valid UTF-8, and String.length(char) == 1 guarantees it's a single Unicode character.
Option 2: Binary Size Union (With Validation)
If you prefer to use binary types directly, you can define a union of 1-4 byte binaries (since UTF-8 characters are 1-4 bytes long), but you still need runtime checks to ensure validity:
# Cover all possible UTF-8 byte lengths (1-4 bytes) @type utf8_binary :: <<_::8>> | <<_::16>> | <<_::24>> | <<_::32>> @type utf8_char :: utf8_binary @spec validate_utf8_char(utf8_char) :: :ok | {:error, :invalid_utf8_char} def validate_utf8_char(char) do if String.valid?(char) and String.length(char) == 1 do :ok else {:error, :invalid_utf8_char} end end
Why Your Original Bit Pattern Failed in Typespec
Elixir's Typespec implementation only supports basic binary type syntax—you can specify the total number of bits/bytes, but not specific bit value patterns or ranges. Those patterns work in pattern matching because the runtime handles them, but the Typespec parser doesn't have logic to parse or validate that kind of constraint.
内容的提问来源于stack exchange,提问作者rewritten

