如何为单字母ASCII字符串(值0-127)进行typepec?

时间:2018-01-08 15:33:58

标签: erlang elixir dialyzer

同样,我如何为“单一”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>> = "○"

成功。)

但是当在typespecs中使用时,编译器会抱怨

== 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

有没有办法将typepec打成这样的位模式?

1 个答案:

答案 0 :(得分:1)

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)