[Haskell] ShortByteString の作り方 まとめ

Strict `ByteString` および Lazy `ByteString` については別記事にしました。

参考「[[Haskell] Strict ByteString の作り方 まとめ - Qiita](https://qiita.com/kerupani129/items/25144e9fac571123d529)」
参考「[[Haskell] Lazy ByteString の作り方 まとめ - Qiita](https://qiita.com/kerupani129/items/0d9c4de961767e24c628)」

各関数の説明等は公式ドキュメント等を参照。

参考「[Data.ByteString.Builder](https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Builder.html)」
参考「[Data.ByteString.Short](https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Short.html)」

※ `ShortByteString` は直接 `putStr` や `putStrLn` することはできません。
※ `Builder` から直接作れる `ByteString` は Lazy `ByteString` のみです。
※ Lazy `ByteString` から直接 `ShortByteString` を作ることはできません。

# 1. `[Word8]` または `Word8` から作る場合

```haskell
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    let shortByteStringA = BSS.pack [99, 97, 116, 32, 227, 129, 173, 227, 129, 147]
    BSC.putStrLn . BSS.fromShort $ shortByteStringA
    print shortByteStringA

    let shortByteStringB = BSS.singleton 99
    BSC.putStrLn . BSS.fromShort $ shortByteStringB
    print shortByteStringB

    let shortByteStringC = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.word8 $ 99
    BSC.putStrLn . BSS.fromShort $ shortByteStringC
    print shortByteStringC
```

```text:実行結果
cat ねこ
"cat \227\129\173\227\129\147"
c
"c"
c
"c"
```

# 2. `String = [Char]` または `Char` から作る場合

## 2.1. 言語拡張を使わない場合

### 2.1.1. ASCII の場合

Strict `ByteString` や Lazy `ByteString` と異なり、`ShortByteString` で `String = [Char]` 用の `pack` 関数は提供されていないため、 `fromIntegral . ord` を用いて `Char` から `Word8` に変換します。

```haskell
import Data.Char (ord)
import Data.String (fromString)

import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    let shortByteStringA = BSS.pack . map (fromIntegral . ord) $ "cat"
    BSC.putStrLn . BSS.fromShort $ shortByteStringA
    print shortByteStringA

    let shortByteStringB = fromString "cat"
    BSC.putStrLn . BSS.fromShort $ shortByteStringB
    print shortByteStringB

    let shortByteStringC = BSS.singleton . fromIntegral . ord $ 'c'
    BSC.putStrLn . BSS.fromShort $ shortByteStringC
    print shortByteStringC

    let shortByteStringD = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.string7 $ "cat"
    BSC.putStrLn . BSS.fromShort $ shortByteStringD
    print shortByteStringD

    let shortByteStringE = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.char7 $ 'c'
    BSC.putStrLn . BSS.fromShort $ shortByteStringE
    print shortByteStringE
```

```text:実行結果
cat
"cat"
cat
"cat"
c
"c"
cat
"cat"
c
"c"
```

### 2.1.2. UTF-8 の場合

やり方によっては UTF-8 を正しく扱えないため注意が必要です。

```haskell
import Data.Char (ord)
import Data.String (fromString)

import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    -- UTF-8 を正しく扱えない
    let shortByteStringA = BSS.pack . map (fromIntegral . ord) $ "cat ねこ"
    BSC.putStrLn . BSS.fromShort $ shortByteStringA
    print shortByteStringA

    let shortByteStringB = fromString "cat ねこ"
    BSC.putStrLn . BSS.fromShort $ shortByteStringB
    print shortByteStringB

    let shortByteStringC = BSS.singleton . fromIntegral . ord $ 'ね'
    BSC.putStrLn . BSS.fromShort $ shortByteStringC
    print shortByteStringC

    -- UTF-8 を正しく扱える
    let shortByteStringD = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.stringUtf8 $ "cat ねこ"
    BSC.putStrLn . BSS.fromShort $ shortByteStringD
    print shortByteStringD

    let shortByteStringE = BSS.pack . BSL.unpack . BSB.toLazyByteString . fromString $ "cat ねこ"
    BSC.putStrLn . BSS.fromShort $ shortByteStringE
    print shortByteStringE

    let shortByteStringF = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.charUtf8 $ 'ね'
    BSC.putStrLn . BSS.fromShort $ shortByteStringF
    print shortByteStringF
```

```text:実行結果
cat mS
"cat mS"
cat mS
"cat mS"
m
"m"
cat ねこ
"cat \227\129\173\227\129\147"
cat ねこ
"cat \227\129\173\227\129\147"
ね
"\227\129\173"
```

※ `ShortByteString` の `fromString` は `String` 型の文字列を UTF-8 として扱いませんが、`Builder` の `fromString` は `String` 型の文字列を UTF-8 として扱います。

## 2.2. `OverloadedStrings` 言語拡張を使う場合

### 2.2.1. ASCII の場合

```haskell
{-# LANGUAGE OverloadedStrings #-}

import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    let shortByteString = "cat"
    BSC.putStrLn . BSS.fromShort $ shortByteString
    print shortByteString
```

```text:実行結果
cat
"cat"
```

### 2.2.2. UTF-8 の場合

やり方によっては UTF-8 を正しく扱えないため注意が必要です。

```haskell
{-# LANGUAGE OverloadedStrings #-}

import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    -- UTF-8 を正しく扱えない
    let shortByteStringA = "cat ねこ"
    BSC.putStrLn . BSS.fromShort $ shortByteStringA
    print shortByteStringA

    -- UTF-8 を正しく扱える
    let shortByteStringB = BSS.pack . BSL.unpack . BSB.toLazyByteString $ "cat ねこ"
    BSC.putStrLn . BSS.fromShort $ shortByteStringB
    print shortByteStringB
```

```text:実行結果
cat mS
"cat mS"
cat ねこ
"cat \227\129\173\227\129\147"
```

※ `ShortByteString` の場合は `String` 型の文字列を UTF-8 として扱いませんが、`Builder` の場合は `String` 型の文字列を UTF-8 として扱います。

# 3. Strict `ByteString` または Lazy `ByteString` から作る場合

```haskell
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    -- 
    let byteString = BS.pack [99, 97, 116, 32, 227, 129, 173, 227, 129, 147]

    let shortByteStringA = BSS.toShort byteString
    BSC.putStrLn . BSS.fromShort $ shortByteStringA
    print shortByteStringA

    let shortByteStringB = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.byteString $ byteString
    BSC.putStrLn . BSS.fromShort $ shortByteStringB
    print shortByteStringB

    -- 
    let lazyByteString = BSL.pack [99, 97, 116, 32, 227, 129, 173, 227, 129, 147]

    let shortByteStringC = BSS.pack . BSL.unpack $ lazyByteString
    BSC.putStrLn . BSS.fromShort $ shortByteStringC
    print shortByteStringC

    let shortByteStringD = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.lazyByteString $ lazyByteString
    BSC.putStrLn . BSS.fromShort $ shortByteStringD
    print shortByteStringD
```

```text:実行結果
cat ねこ
"cat \227\129\173\227\129\147"
cat ねこ
"cat \227\129\173\227\129\147"
cat ねこ
"cat \227\129\173\227\129\147"
cat ねこ
"cat \227\129\173\227\129\147"
```

参考「[unpack - Data.ByteString.Lazy](https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Lazy.html#v:unpack)」

# 4. 空文字列の場合

```haskell
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    let shortByteStringA = BSS.empty
    BSC.putStrLn . BSS.fromShort $ shortByteStringA
    print shortByteStringA

    let shortByteStringB = mempty
    BSC.putStrLn . BSS.fromShort $ shortByteStringB
    print shortByteStringB

    let shortByteStringC = BSS.pack . BSL.unpack . BSB.toLazyByteString $ mempty
    BSC.putStrLn . BSS.fromShort $ shortByteStringC
    print shortByteStringC
```

```text:実行結果

""

""

""
```

# 5. その他の型から作る場合

他にも様々な型の値から `ShortByteString` を作ることができます。

ここでは例として一部のみ載せます。他の作り方に関しては公式ドキュメント等を参照して下さい。

参考「[Data.ByteString.Builder](https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Builder.html)」
参考「[Data.ByteString.Short](https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Short.html)」

```haskell
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS

main :: IO ()
main = do

    let shortByteStringA = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.word32BE $ 0x63617420
    BSC.putStrLn . BSS.fromShort $ shortByteStringA
    print shortByteStringA

    let shortByteStringB = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.shortByteString $ shortByteStringA
    BSC.putStrLn . BSS.fromShort $ shortByteStringB
    print shortByteStringB

    let shortByteStringC = BSS.pack . BSL.unpack . BSB.toLazyByteString . BSB.word32HexFixed $ 0x63617420
    BSC.putStrLn . BSS.fromShort $ shortByteStringC
    print shortByteStringC
```

```text:実行結果
cat
"cat "
63617420
"63617420"
63617420
"63617420"
```