[Elixir] "Hello"と'Hello'

この記事は [Elixir その2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/elixir2) 14日目です。

先日は@torifukukaiou さんの「[GigalixirでPORTを4000以外の値にするのはだめよ (Elixir)](https://qiita.com/torifukukaiou/private/a570e8baa337c73f011a)」でした。
@torifukukaiou さん１３日間お疲れさまでした。しばらく休憩してください。

Elixir楽しいです。少しでも多くの方とその楽しみを共有したいものです。日頃、色んな人のメモにお世話になっているので、僕も勉強した内容をどんどんアウトプットしていこうと思います。

## はじめに

[Elixir](https://elixir-lang.org/)ではシングルクオート（`'Hello'`）とダブルクオート（`"Hello"`）が同義ではないと何となく分かっていながら、シングルクオートを使う機会が今まで全くありませんでした。使わないので結局、よくわからないまま放置してありました。

が、[Nerves](https://www.nerves-project.org/)でIoTのシリアル通信の勉強をしていて戸惑うことがあったので、学んだ内容を軽くメモをしました。

因みに[Nerves](https://www.nerves-project.org/)でのIoT開発/電子工作はかなり楽しいので興味のある方には[#NervesJP Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nervesjp)をおすすめします。

## 戸惑ったこと

シリアル通信でStringの文字一つ一つを[バイト](https://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88_(%E6%83%85%E5%A0%B1))として送信する際に、StringからCharlistへの変換が必要であること。それを理解してなかった。

```elixir
# データ送信関数イメージ
iex> send_byte = fn x -> IO.puts("#{x}を送信") end

# 失敗イメージ
iex> "Hello" |> Enum.each(send_byte)
** (Protocol.UndefinedError) protocol Enumerable not implemented for "Hello" of type BitString. This protocol is implemented for the following type(s): RingLogger.CircularBuffer, HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
    (elixir 1.11.2) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir 1.11.2) lib/enum.ex:141: Enumerable.reduce/3
    (elixir 1.11.2) lib/enum.ex:3461: Enum.each/2

# 成功イメージ
iex> 'Hello' |> Enum.each(send_byte)
72を送信
101を送信
108を送信
108を送信
111を送信
:ok
```

## `""`と`''`の違いを確認

### [マッチ演算子`=`](https://elixir-lang.org/getting-started/pattern-matching.html#the-match-operator)

```elixir
"Hello" = 'Hello'
** (MatchError) no match of right hand side value: 'Hello'
```

### [比較演算子`==`](https://elixir-lang.org/getting-started/basic-operators.html)

```elixir
"Hello" == 'Hello'
false
```

### `IO.inspect`

```elixir
iex> IO.inspect("Hello")
"Hello"

iex> IO.inspect('Hello')
'Hello'
```

なんの情報も得られませんでした。

### iexの[`i` ヘルパー関数](https://hexdocs.pm/iex/IEx.Helpers.html)

```
iex> i "Hello"

Term
  "Hello"
Data type
  BitString
Byte size
  5
Description
  This is a string: a UTF-8 encoded binary. It's printed surrounded by
  "double quotes" because all UTF-8 encoded code points in it are printable.
Raw representation
  <<72, 101, 108, 108, 111>>
Reference modules
  String, :binary
Implemented protocols
  Collectable, IEx.Info, Inspect, List.Chars, String.Chars
```

```
iex> i 'Hello'

Term
  'Hello'
Data type
  List
Description
  This is a list of integers that is printed as a sequence of characters
  delimited by single quotes because all the integers in it represent printable
  ASCII characters. Conventionally, a list of Unicode code points is known as a
  charlist and a list of ASCII characters is a subset of it.
Raw representation
  [72, 101, 108, 108, 111]
Reference modules
  List
Implemented protocols
  Collectable, Enumerable, IEx.Info, Inspect, List.Chars, String.Chars
```

具体的な違いが見えてきました。

```elixir
# a string: a UTF-8 encoded binary
"Hello" = <<72, 101, 108, 108, 111>>

# a list of integers
'Hello' = [72, 101, 108, 108, 111]
```

データタイプが全く違うんですね。

```elixir
is_binary("Hello") #=> true
is_binary('Hello') #=> false

is_bitstring("Hello") #=> true
is_bitstring('Hello') #=> false

is_list("Hello") #=> false
is_list('Hello') #=> true
```

## `"Hello"`を`'Hello'`に変換

[`Kernel.to_charlist`](https://hexdocs.pm/elixir/Kernel.html#to_charlist/1)で可能。

```elixir
"Hello" |> to_charlist                    #=> 'Hello'
<<72, 101, 108, 108, 111>> |> to_charlist #=> 'Hello'
```

## `'Hello'`を`"Hello"`に変換

[`Kernel.to_string`](https://hexdocs.pm/elixir/Kernel.html#to_string/1)で可能。

```elixir
'Hello' |> to_string                  #=> "Hello"
[72, 101, 108, 108, 111] |> to_string #=> "Hello"
```

## `IO.inspect`のオプションを変えてみる

```elixir
iex> IO.inspect("Hello", binaries: :as_binaries)
<<72, 101, 108, 108, 111>>
"Hello"

iex> IO.inspect('Hello', charlists: :as_lists)
[72, 101, 108, 108, 111]
'Hello'
```

## もう一回正しい理解でデータ送信

```elixir
iex> send_byte = fn x -> IO.puts("#{x}を送信") end
#Function<44.97283095/1 in :erl_eval.expr/5>

iex> "Hello" |> to_charlist |> Enum.each(send_byte)
72を送信
101を送信
108を送信
108を送信
111を送信
:ok
```

## さいごに

因みにやりたかったことはLCDに書き込むときの[こういうやつ](https://github.com/mnishiguchi/lcd_display/blob/7a4d46adb48261733decd979cd223144034bcb21/lib/lcd_display/hd44780_i2c.ex#L133-L142)です。

ここまでまとめてみて、やっと基本が分かってきました。更に[Elixir - Getting Started](https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html)の例のようなUnicodeに対応する場合は文字によっては隠れたバイトがあるので、もう少しややこしい場合があるようです。

[Elixir その2 Advent Calendar 2020 初日](https://qiita.com/torifukukaiou/items/badb4725a9c17788f8b1)の`[87, 101, 32, 97, 114, 101, 32, 116, 104, 101, 32, 65, 108, 99, 104, 101, 109, 105, 115, 116, 115, 44, 32, 109, 121, 32, 102, 114, 105, 101, 110, 100, 115, 33]`
に改めて戻ってみると、おもしろいです。

明日は「[[Elixir] GenServerのCallとCast](https://qiita.com/mnishiguchi/private/542ec8bcea454b00a32a)」です。引き続き、Elixirを楽しみましょう。

- [Elixir その1 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/elixir)
- [Elixir その2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/elixir2)
- [【毎日自動更新】QiitaのElixir LGTMランキング！](https://qiita.com/torifukukaiou/items/1edb3e961acf002478fd) by @torifukukaiou