Monalog

3132人のHaskell好きが訪れました

リンク記法サポート

2026/02/08

これはMonalogへのリンクですか?無事レンダリングされていればMarkdownのリンク記法をサポートできている。リンク記法はコードや強調と異なり2つのパートに分かれている。

[ラベル](URL)

パース自体は簡単だが工程がちょっと多い。まずは愚直に case..ofをネストして書いた。愚直っていっても、すんなり書けたわけじゃない。

parseLink :: Text -> [Inline]
parseLink text =
  case T.breakOn "]" (T.drop 1 text) of
    (_, "") -> [Plain text]
    (label, next) ->
      case T.isPrefixOf "(" (T.drop 1 next) of
        False -> [Plain text]
        True ->
          case T.breakOn ")" (T.drop 2 next) of
            (_, "") -> [Plain text]
            (url, rest) -> Link (label, url) : parseInline (T.drop 1 rest)

いかにも分かりにくいコードの認識がありながらも、今はこれが精一杯。要は、] ( ) のリンク記法の記号をひたすら探索していくだけの処理。(先頭の [ 記号はすでにパース済みで渡ってくる)

こんな感じでネストしていると、途中の値がどうなっているのか謎でデバッグしにくかったが、調べたら Debug.Trace を使うと良いということを知った。

trace ("text: " <> text) $ something

AIにレビューをしたもらうと、case..of のネストを避けるためにMaybeを使えと言う。また出たよMaybe。世の中、曖昧だらけだ。

parseLink text = fromMaybe [Plain text] $ do
  let (label, next) = T.breakOn "]" (T.drop 1 text)
  urlNext <- T.stripPrefix "(" (T.drop 1 next)
  let (url, rest) = T.breakOn ")" urlNext
  guard (not $ T.null rest)
  pure $ Link (label, url) : parseInline (T.drop 1 rest)

fromMaybeを使うことでNothingだったときのデフォルト値を設定できる。これは便利。さらに guardを組み合わせると途中離脱させることも可能。これでいいかなと思ってさらにAIにレビューしてもらったら関数分割しろよって怒られたので、分割をしてみた。

parseLink :: Text -> [Inline]
parseLink text = fromMaybe [Plain text] $ do
  (label, afterLabel) <- extractLinkLabel text
  (url, rest) <- extractLinkUrl afterLabel
  pure $ Link (label, url) : parseInline (T.drop 1 rest)

extractLinkLabel :: Text -> Maybe (Text, Text)
extractLinkLabel text = do
  let (label, next) = T.breakOn "]" (T.drop 1 text)
  guard (not $ T.null next)
  pure (label, next)

extractLinkUrl :: Text -> Maybe (Text, Text)
extractLinkUrl text = do
  next <- T.stripPrefix "(" (T.drop 1 text)
  let (url, rest) = T.breakOn ")" next
  guard (not $ T.null rest)
  pure (url, rest)

do式のおかげで、途中で想定外のデータがあったときも強制終了されるので気にせず関数分割して処理できる。良い…のかどうなのか。何もわからない雰囲気で書いている。

戻る