Open35

Haskellやる

とさとさ

文字列は[Char]型(Stringの糖衣構文)
headは配列から先頭の値を取得する関数

GHCi, version 8.10.7: https://www.haskell.org/ghc/  :? for help
Prelude> name = "tosa"
Prelude> :t name
name :: [Char]
Prelude> :t head name
head name :: Char
Prelude> head name
't'
とさとさ

雰囲気で$を使うとコンパイル通るからつけてるけどこれはなに?

Prelude> print $ head name
't'

なるほど
https://www.tohoho-web.com/ex/haskell.html

括弧の代わりに $ を用いることもできます。$ は $ から行末までを (...) で囲むのと同じ意味になります。

とさとさ

do構文

module Main where

import Lib

main = do
	putStrLn "hello"
	putStrLn "world"

output

~/pra  stack run pra-exec                                                                                                    3524ms  木  5/ 5 22:20:37 2022
hello
world

これでも大丈夫

main = putStrLn "hello" >> putStrLn "world"
とさとさ

Either型

Prelude> :t either
either :: (a -> c) -> (b -> c) -> Either a b -> c

Either型の実装

head' :: [a] -> Either String a
head' [] = Left "emptylist"
head' (x:xs) = Right x

main = do
	case head' [1, 2, 3] of
		Left s -> putStrLn $ show s
		Right s -> putStrLn $ show s
	case head' "helloworld" of
		Left s -> putStrLn $ show s
		Right s -> putStrLn $ show s
	case head' [] of
		Left s -> putStrLn $ show s
		Right s -> putStrLn $ s

参考
https://qiita.com/Izawa_/items/ed0579a0e7d93e5c09cc
https://stackoverflow.com/questions/26051376/how-to-print-the-type-either-string-io-string

とさとさ

instanceは型クラスの実装や!!!
class Semigroup的なのがあってそれを(Either a b)に実装しているんやな
http://walk.northcol.org/haskell/type-classes/

110416110416

class Semigroup 的なのがあって

あんまりオブジェクト指向のクラスと混ぜて考えないほうがいいと思われ. 僕は数学の本にしばしばみかける「××を満たすものを○○と呼ぶ」的なメンタルモデルで考えてます.

とさとさ

このように演算子が一個だけ欲しいと思ったら、それは多分 Monoid だ。

monoidかmonadに見えた(?)、monadもmonoidだけど...

本文中にあるこれがすごく良き

> Metrics 1 2 <> Metrics 3 4
Metrics {rx = 4, ts = 6}
とさとさ

代数学での包含関係は半群->モノイド->群なので

GHC 8.4ではSemigroupがMonoidのスーパークラスとなり、Metricsに対する(<>)の定義がないために、エラーが出たという訳だ。

SemigroupをMonoidのスーパークラスにしたいよね的なモチベーションがあったらしい。

とさとさ

あれ、ListだとputStrLnできないな
[Char]だとできるけど[Int]だとできない

とさとさ

isEmptyをEither返却にする。functorがあるからfmapが使えるか試す

isEmpty' :: [a] -> Either String [a]
isEmpty' [] = Left  "empty"
isEmpty' x = Right x

main :: IO ()
main = do
        case isEmpty' "helloworld" of
                Left s -> putStrLn s
                Right s -> putStrLn s
とさとさ

あんまり綺麗なコードじゃないけどLeftの場合とRightの場合にfunctorを使用してみた

isEmpty' :: [a] -> Either String [a]
isEmpty' [] = Left  "empty"
isEmpty' x = Right x

main :: IO ()
main = do
        case (++"[END_SENTENCE]") <$> isEmpty' "helloworld" of
                Left s -> putStrLn s
                Right s -> putStrLn s
	case (++"[END_SENTENCE]") <$> isEmpty' [] of
		Left  s -> putStrLn s
		Right s -> putStrLn s
 ~/pra  stack run pra-exec                    2104ms  木  5/ 5 23:13:25 2022
helloworld[END_SENTENCE]
empty
110416110416

ちな binfunctor という left と right に map とかが生えた子もいます

とさとさ

<> 結合律をもつ何らかの演算と考えておけばいいかな

a <> (b <> c) = (a <> b) <> c

とさとさ

ちゃんと満たしていそうで良き

Prelude> ([1] <> [2] ) <> [3]
[1,2,3]
Prelude> [1] <> ( [2]  <> [3] )
[1,2,3]
とさとさ

Eitherの場合ですごい雑コード描いちゃったけどこれは何の演算になるんだろう

Prelude> a = Right 2
Prelude> b = Right 12
Prelude> c = Left 12
Prelude> a <> ( b <> c)
Right 2
Prelude> a <> (b <> c)
Right 2
Prelude> (a <> b) <> c
Right 2
とさとさ

したの実装の謎はとけたな。 a <> _ = aだから各コードの左側の値が返却されていて結合律をきちんと満たしている

instance Semigroup (Either a b) where
    Left _ <> b = b
    a      <> _ = a
とさとさ

わかった、左側がLeft _ の場合は右側の値を返却している感じだ。
謎が解けた

Prelude> c <> c
Left 12
Prelude> c <> a
Right 2
Prelude> c <> b <> a
Right 12
とさとさ

一番上のqiitaの構文解析の記事に戻って計算機をつくる。import Text.Parsecと呼ばれるライブラリを使用している

expr :: ParsecT String u Data.Functor.Identity.Identity Int
expr = do
        x <- number
        fs <- many $ do
                char '+'
                y <- number
                return (+ y)
                <|> do
                        char '-'
                        subtract <$> number
		<|> do
			char '*'
			y <- number
			return (* y)
		<|> do
			char '/'
			y <- number
			return (`div` y)
	return $ foldl (\x f -> f x) x fs
とさとさ

<*, *>の記法についての説明がされている。letter <* digit, letterであるならばletter(文字のみが残るので出力はacletter *> digit, letterであるならばdigitとletterの並びが残るので1cになるのかな

import Text.Parsec
import Control.Applicative ((<*), (*>))

main = do
    parseTest (sequence [letter,    digit, letter]) "a1c"
    parseTest (sequence [letter <*  digit, letter]) "a1c"
    parseTest (sequence [letter  *> digit, letter]) "a1c"
    parseTest (sequence [letter  >> digit, letter]) "a1c"
とさとさ

これでかけるのすごいけど理解が及んでない...

eval :: (Applicative f, Foldable t) => f b -> f (t (b -> b)) -> f b
eval m fs = foldl (\x f -> f x) <$> m <*> fs
apply f m = flip f <$> m

expr = eval term $ many $ 
	char '+' *> apply (+) term
	<|> char '-' *> apply (-) term


term = eval number $ many $ 
	char '*' *> apply (*) number
	<|> char '/' *> apply div number

number :: ParsecT String u Data.Functor.Identity.Identity Int
number = read <$> many1 digit