generated at
purescript-halogen-realworldのcode reading

アーキテクチャを学びたいmrsekut
これが目的
隅から隅まで理解したいわけではない
halogenをちゃんと理解しているわけではないので、詰まったらtutorialに戻る、という感じで読んでいく

内容はRealWorldなので、それを知っていれば何がやりたいのかはわかるはず
色々解説してるっぽい記事あった
読んでみたらめちゃくちゃよかったmrsekutmrsekutmrsekut

Use Cases
e.g. userをfollowする
Data
e.g. UsersやArticlesなどのEntity
Tranformations
stateから別のstateに変換する関数
小さいaction


dataを3種類に分類する
Entities
永続的なidを持つデータ
同じidを持っているなら同じものである
e.g. User, Article
Values
同じ値なら同じものである
e.g. Email, Username, List
Lifecycles
状態を表すデータ
EntityやValueを含むデータ型
これはちょっと具体的にイメージつかない #??
具体例を見たいmrsekut
設計方針
データを設計するための原則
サポートする必要のあるビジネス的なプロセスに依ってデータ型が決まり、
データ型によって、これらのプロセスを関数として実装する方法が決まる
use caseを理解していないと適切なデータ型は作成できない
適切なデータ型がないと関数が混乱する
ここの文章めちゃくちゃ良いなmrsekut
newtype CustomerId = CustomerId Natural
newtypeでIdを作ることで、「idでの算術」などを不可能にする
必ずしも完全なデータ型を作る必要はない
module内に閉じ込めて、適切に関数を提供すれば安全性は担保できる
EntityとValue
ProcessとLifecycle
fetchの話であればpurescript-remotedataとかも使ってみる


アーキテクチャについて
純粋な関数型プログラミングの基本原則は、エフェクトとデータを可能な限り分離すること
コードの大部分は副作用のない関数とデータとして記述されており、アプリケーションの境界でのみEffectが現れる
>型クラスは、取得または処理するために使用するメカニズムではなく、操作する情報を記述するように設計する必要があります
わかるようでわからない
普通に型クラス定義したらそうならん?
具体的な接続先を意識しないようなinterfaceを定義しろということか
アクセス先がRESTでもGraphQLでもlocalStrageでも使えるようなinterfaceを型クラス内に定義する



Capability内で HalogenM st act slots msg m にだけinstance作っているのはなぜ?
これなしで書いてみたらわかるmrsekut
Componentは、HalogenMを返り値にするので、その中でcapabilityのmethodを使おうと思うと、毎回liftしないといけない
単純にそれがめんどいので、 HalogenM に対してinstanceを予め定義している


あー、こういうのもLayer 3的ではあるのか
purs(hs)
newtype LogMessage = LogMessage String logMessage :: forall m . Monad m => m DateTime -- | How should we fetch the current time? -> (LogMessage -> m Unit) -- | How should we write this message? -> LogType -- | What kind of log is this? -> String -- | What is the input string? -> m Unit logMessage getTime writeLog logType msg = do t <- fmtDateTime <$> getTime let msg' = LogMessage $ case logType of Debug -> "[DEBUG: " <> t <> "]\n" <> msg Info -> "[INFO: " <> t <> "]\n" <> msg Error -> "[ERROR: " <> t <> "]\n" <> msg writeLog msg'
これは、Effectにも依存していない、純粋な関数型プログラミングと言える
これを使って、AppMのinstanceを定義する
purs(hs)
instance LogMessages AppM where logMessage log = logMessage log
ここで初めて、AffやEffectと関係を持つ
薄い命令形shell

AppMは通常のMonadに加えて以下のようなものもderivingしておく
purs(hs)
derive newtype instance MonadEffect AppM derive newtype instance MonadAff AppM derive newtype instance MonadStore Action Store AppM
上2つはまあわかる
一番下は MonadAsk を実装しているようなもの
Storeはhalogenが提供している別のlibrary

The only tricky part is .. lのぶぶんで、Storeはtypeで定義するが、
これはinstanceを導出できない
そこで Type.Equality を利用すればできる
理由はここを見よ、みたいにかいているが特に説明はない
Type.Equalityって、hsのData.Type.Equalityのことか?
>The beauty of these instances is that our logging capability is decoupled from its implementation in AppM
これは、capablity同士が独立しているから、1つの変更が他のcapablityのAppM実装に影響を与えない、という意味かな #??
>Testing our new logger capability
テストの書き方

EnvはこのPRでStoreにrenameされている
Store内のcommentにReduxのstoreと似ている、と書いているが、微妙に語弊あると思うmrsekut
global stateと言う意味では同じだけど、実行中に更新されること無いから
あー、でもmainでの注入時にActionやreduceという名前の関数を使ってるのか。
ならredux的と言っても良さそう
一発目だけの話なので、誤解は与えそうだけど
この辺の話はstoreのlibraryを見るのが良さそう


Componentsに関して
Reactと比べるとややモノリシックな構成になる
componentも純粋に保つ
capabilityへアクセスすることもできる
型で表現すればいい
できるだけ、状態を持つcomponentではなく、純粋なHTMLとして作るのが良い
もうちょい見えてきてから再読しようmrsekutmrsekut



API系
Endpoint.purs
Request.purs
なぜ自分で RequestMethod 型を定義しているのかと言うと、 Data.HTTP.Method とかのは汎用的にするために冗長になっているから
利用に過不足が無いように自分で定義する
Utils.purs



Component周りの
H.forkとかまだよくわかっていない






dir
. ├── assets │ ├── index.html │ └── logo.png ├── dev │ ├── index.html │ └── logo.png ├── index-dev.js ├── index.js ├── LICENSE ├── package-lock.json ├── package.json ├── packages.dhall ├── README.md ├── shell.nix ├── spago.dhall ├── src │ ├── Api │ │ ├── Endpoint.purs │ │ ├── Request.purs │ │ └── Utils.purs │ ├── AppM.purs -- Application Monad │ ├── Capability -- Capability (後述) │ ├── Component │ │ ├── HigherOrder │ │ │ └── Connect.purs │ │ ├── HTML │ │ │ ├── ArticleList.purs │ │ │ ├── Footer.purs │ │ │ ├── Header.purs │ │ │ └── Utils.purs │ │ ├── Part │ │ │ ├── FavoriteButton.purs │ │ │ └── FollowButton.purs │ │ ├── RawHTML.js │ │ ├── RawHTML.purs │ │ ├── Router.purs │ │ ├── TagInput.purs │ │ └── Utils.purs │ ├── Data -- ValueとEntityの定義(後述) │ ├── Store.purs │ ├── Foreign │ │ ├── Marked.js │ │ └── Marked.purs │ ├── Form │ │ ├── Field.purs │ │ └── Validation.purs │ ├── Main.purs │ └── Page │ ├── Editor.purs │ ├── Home.purs │ ├── Login.purs │ ├── Profile.purs │ ├── Register.purs │ ├── Settings.purs │ └── ViewArticle.purs └── test └── Main.purs


Capability
LogMessages.purs
Navigate.purs
Now.purs
Resource
Article.purs
Comment.purs
Tag.purs
User.purs

Data
User ??
user idで識別
Userを表すEntity
loginなど認証が必要なactionに使われる
Avatar
Profile
usernameで識別
Userに関する公開情報を表すEntity
記事やコメントを書いたUserとか、followの対象を表す
Article
slugで識別
記事のEntity
>It refers to an author,
itが何を指しているのかわからん #??
Followの状態はBoolでは表さない
いいねmrsekut
purs(hs)
data Relation = Following | NotFollowing | You
自分のページということも型に含める
また、この仕様は元のrealworldの仕様と異なるため、 ProfileRep 型の外に定義する
ProfileRep Relation を加えたものとして、 Author 型を定義する
Comment
identified by an id combined with the slug of a particular article. It also refers to an author.
Email
Log
PaginatedArray
PreciseDateTime
Route
Username
smat constructorの名前は parse なのかmrsekut
mkUsername ではないんだな