動機
私は現在、Haskell でTaskJugglerのようなものを試して実装するためのちょっとした趣味のプロジェクトに取り組んでいます。これは主に、ドメイン固有言語を書く実験です。
Project
私の現在の目標は、関連する とともに、の記述を構築するための小さな DSL を持つことTask
です。階層はまだありませんが、それが私の次の拡張になります。現在、次のデータ型があります。
data Project = Project { projectName :: Text
, projectStart :: Day
, projectEnd :: Day
, projectMaxHoursPerDay :: Int
, projectTasks :: [Task]
}
deriving (Eq, Show)
data Task = Task { taskName :: Text }
deriving (Eq, Show)
そこには何もおかしなことはありません。きっと同意していただけると思います。
ここで、プロジェクト/タスクを構築するための DSL を作成したいと考えています。モナドを使ってタスクを構築することはできますWriter [Task]
が、これではうまくスケーリングできません。現在、次のことができる可能性があります。
project "LambdaBook" startDate endDate $ do
task "Web site"
task "Marketing"
Whereproject :: Text -> Date -> Date -> Writer [Task] a
は、 を実行しWriter
てタスクのリストを取得し、 に対して 8 などのデフォルト値を選択しますprojectMaxHoursPerDay
。
しかし、後で次のようなことができるようになりたいと思います。
project "LambdaBook" $ do
maxHoursPerDay 4
task "Web site"
task "Marketing"
だから私はmaxHoursPerDay
についての(将来の)プロパティを指定するために使用していますProject
。必要なものすべてをキャプチャできないWriter
ため、これには a を使用できなくなりました。[Task]
この問題を解決するには、次の 2 つの可能性があります。
「オプションの」プロパティを独自のモノイドに分離
次のように分割できProject
ます。
data Project = Project { projectName, projectStart, projectEnd, projectProperties }
data ProjectProperties = ProjectProperties { projectMaxHoursPerDay :: Maybe Int
, projectTasks :: [Task]
}
これで、インスタンスを作成できますMonoid ProjectProperties
。実行するWriter ProjectProperties
と、ビルドに必要なすべてのデフォルト設定を行うことができますProject
。Project
埋め込む必要がある理由はないと思いますProjectProperties
-上記と同じ定義を持つことさえできます。
バインド可能なファンクターを使用するSemigroup m => Writer m
でProject
はありませんがMonoid
、確かに にすることができますSemigroup
。名前/開始/終了はFirst
、maxHoursPerDay
is Last
、およびprojectTasks
is[Task]
です。Writer
の上にモナドを持つことはできませんが、バインド可能なファンクターSemigroup
を持つことはできます。Writer
実際の質問
最初の解決策 - 専用の「プロパティ」Monoid
- を使用すると、選択したコストでモナドの全機能を使用できます。オーバーライド可能なプロパティをProject
とProjectProperties
で複製できます。後者は各プロパティを適切なモノイドでラップします。または、モノイドを 1 回記述して内部に埋め込むこともできますが、Project
型の安全性はあきらめます (プロジェクト計画を実際に作成するときでmaxHoursPerDay
なければなりません! )。Just
バインド可能なファンクターは、コードの重複を取り除き、型の安全性を保持しますが、構文糖衣を放棄するという当面のコストと、( return
/がないためにpure
) 作業が面倒になるという長期的なコストがかかる可能性があります。
http://hpaste.org/82024 (バインド可能なファンクター用) とhttp://hpaste.org/82025 (モナド アプローチ用)に両方のアプローチの例があります。これらの例は、この SO 投稿 (既に十分な大きさでした) の内容を少し超えており、. 願わくば、これが私がDSL でどこまで(または)行く必要があるのかを示してくれることを願っています。Resource
Task
Bind
Monad
バインド可能なファンクターの適切な用途を見つけたことに興奮しているので、あなたの考えや経験を聞いてうれしいです.