如何为单个字母 ASCII 字符串(值 0-127)指定类型?
How can I typespec for a single letter ASCII string (value 0-127)?
同样,我如何为 "single" UTF8 字符指定类型?
在类型定义中,我可以使用泛型 "any string" 或 "any utf8 string" 和
@type tile :: String.t # matches any string
@type tile :: <<_::8>> # matches any single byte
但我似乎无法将第一位匹配为 0
@type tile :: <<0::1, _::7>>
单个 UTF 位序列的情况是
@type tile :: <<0::1, _::7>> |
<<6::3, _::5, 2::2, _::6>> |
<<14::4, _::4, 2::2, _::6, 2::2, _::6>> |
<<30::5, _::3, 2::2, _::6, 2::2, _::6, 2::2, _::6>>
(使用模式匹配时这些位模式匹配,例如
<<14::4, _::4, 2::2, _::6, 2::2, _::6>> = "○"
成功。)
但是在类型规范中使用时,编译器会抱怨
== Compilation error in file lib/board.ex ==
** (ArgumentError) argument error
(elixir) lib/kernel/typespec.ex:1000: Kernel.Typespec.typespec/3
(elixir) lib/kernel/typespec.ex:1127: anonymous fn/4 in Kernel.Typespec.typespec/3
(elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/kernel/typespec.ex:1127: Kernel.Typespec.typespec/3
(elixir) lib/kernel/typespec.ex:828: anonymous fn/4 in Kernel.Typespec.typespec/3
(elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/kernel/typespec.ex:828: Kernel.Typespec.typespec/3
(elixir) lib/kernel/typespec.ex:470: Kernel.Typespec.translate_type/3
有没有什么方法可以像这样对某些位模式进行类型规范?
You cannot typespec on binary patterns 仅基于二进制文件的唯一事实。即使您可以定义此类规范,我也不认为 Dialyzer 足够复杂,可以在此类匹配中发现故障。您只能在运行时使用守卫和模式匹配来实现此类行为,例如:
def unicode?(<<0::size(1), a::size(7)>>), do: true
def unicode?(<<6::3, _::5, 2::2, _::6>>), do: true
def unicode?(<<14::4, _::4, 2::2, _::6, 2::2, _::6>>), do: true
def unicode?(<<30::5, _::3, 2::2, _::6, 2::2, _::6, 2::2, _::6>>), do: true
def unicode?(str) when is_binary(str), do: false
不幸的是,据我所知,守卫中没有位模式,您只能使用 binary_part/3
匹配整个字节,但没有对位执行相同操作的函数。所以你能得到的最接近的东西是这样的(未经测试这是否有效甚至编译,但让你大致了解可能的情况):
defguardp is_valid_utf_part(code) when code in 0b10000000..0b10111111
defguard is_unicode(<<ascii>>) when ascii in 0b0000000..0b01111111
defguard is_unicode(<<first, second>>)
when first in 0b11000000..0b11011111
and is_valid_utf_part(second)
defguard is_unicode(<<first, second, third>>)
when first in 0b11100000..0b11101111
and is_valid_utf_part(second)
and is_valid_utf_part(third)
defguard is_unicode(<<first, second, third, fourth>>)
when first in 0b11110000..0b11110111
and is_valid_utf_part(second)
and is_valid_utf_part(third)
and is_valid_utf_part(fourth)
同样,我如何为 "single" UTF8 字符指定类型?
在类型定义中,我可以使用泛型 "any string" 或 "any utf8 string" 和
@type tile :: String.t # matches any string
@type tile :: <<_::8>> # matches any single byte
但我似乎无法将第一位匹配为 0
@type tile :: <<0::1, _::7>>
单个 UTF 位序列的情况是
@type tile :: <<0::1, _::7>> |
<<6::3, _::5, 2::2, _::6>> |
<<14::4, _::4, 2::2, _::6, 2::2, _::6>> |
<<30::5, _::3, 2::2, _::6, 2::2, _::6, 2::2, _::6>>
(使用模式匹配时这些位模式匹配,例如
<<14::4, _::4, 2::2, _::6, 2::2, _::6>> = "○"
成功。)
但是在类型规范中使用时,编译器会抱怨
== Compilation error in file lib/board.ex ==
** (ArgumentError) argument error
(elixir) lib/kernel/typespec.ex:1000: Kernel.Typespec.typespec/3
(elixir) lib/kernel/typespec.ex:1127: anonymous fn/4 in Kernel.Typespec.typespec/3
(elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/kernel/typespec.ex:1127: Kernel.Typespec.typespec/3
(elixir) lib/kernel/typespec.ex:828: anonymous fn/4 in Kernel.Typespec.typespec/3
(elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/kernel/typespec.ex:828: Kernel.Typespec.typespec/3
(elixir) lib/kernel/typespec.ex:470: Kernel.Typespec.translate_type/3
有没有什么方法可以像这样对某些位模式进行类型规范?
You cannot typespec on binary patterns 仅基于二进制文件的唯一事实。即使您可以定义此类规范,我也不认为 Dialyzer 足够复杂,可以在此类匹配中发现故障。您只能在运行时使用守卫和模式匹配来实现此类行为,例如:
def unicode?(<<0::size(1), a::size(7)>>), do: true
def unicode?(<<6::3, _::5, 2::2, _::6>>), do: true
def unicode?(<<14::4, _::4, 2::2, _::6, 2::2, _::6>>), do: true
def unicode?(<<30::5, _::3, 2::2, _::6, 2::2, _::6, 2::2, _::6>>), do: true
def unicode?(str) when is_binary(str), do: false
不幸的是,据我所知,守卫中没有位模式,您只能使用 binary_part/3
匹配整个字节,但没有对位执行相同操作的函数。所以你能得到的最接近的东西是这样的(未经测试这是否有效甚至编译,但让你大致了解可能的情况):
defguardp is_valid_utf_part(code) when code in 0b10000000..0b10111111
defguard is_unicode(<<ascii>>) when ascii in 0b0000000..0b01111111
defguard is_unicode(<<first, second>>)
when first in 0b11000000..0b11011111
and is_valid_utf_part(second)
defguard is_unicode(<<first, second, third>>)
when first in 0b11100000..0b11101111
and is_valid_utf_part(second)
and is_valid_utf_part(third)
defguard is_unicode(<<first, second, third, fourth>>)
when first in 0b11110000..0b11110111
and is_valid_utf_part(second)
and is_valid_utf_part(third)
and is_valid_utf_part(fourth)