【結論】Textの中のStringにmap :: String -> StringするにはProfunctorが一番!

 ときどきTextにpack,unpackを介してString -> Stringしたくなるシチュエーションに出会う。 でも普通に関数合成すると、なんだか格好悪いんだよね。

{-# LANGUAGE OverloadedStrings #-}

import Data.Char (toUpper)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO

-- Data.TextのtoUpperを使えとか言っちゃだめ! そしたら別の適切な例を教えて!
upper :: String -> String
upper = map toUpper

x :: Text
x = "nico"

main :: IO ()
main = do
  let niconico = T.pack . upper $ T.unpack x -- 格好悪い
  TIO.putStrLn niconico

 Profunctordimapを使う。 (->)Profunctorを使えばdomainとcodomainの変更を一気にできるので、 Text -> StringString -> StringString -> Textを格好良くできる!(まあ中身は👆と全くおんなじなんだけどでも見栄えいい)

 これはその型と同型の型への操作に一般化できますので、newtypeにも応用できます。

{-# LANGUAGE OverloadedStrings #-}

import Data.Char (toUpper)
import Data.Profunctor (dimap)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO

upper :: String -> String
upper = map toUpper

x :: Text
x = "nico"

main :: IO ()
main = do
  let niconico = dimap T.unpack T.pack upper x -- 平坦な構文に見えるので、見やすい
  TIO.putStrLn niconico
-- newtype Identity a = Identity { runIdentity :: a }
dimap runIdentity Identity f x

その他

  • Foundation.Collection InnerFunctor - Hackage
    • 最初に考えたアプローチがちょうどこれで、でもTextのインスタンスがなかった
  • Data.MonoTraversable - MonoFunctor - Hackage
    • InnerFunctorと全く同じアプローチでTextインスタンスもある、けどElementCharだった。 普通に考えればそうだよね
    • Textのnewtypeを作ってもいいけど、それならpack,unpackした方が早いと思ったのでやめた

筆者プロフィール

my-latest-logo

aiya000(あいや)

せつラボ 〜圏論の基本〜」 「せつラボ2~雲と天使と関手圏~」 「矢澤にこ先輩といっしょに代数!」を書いています!

強い静的型付けとテストを用いて、バグを防ぐのが好き。Haskell・TypeScript。