おそらく最良の方法は、あなたのAppM
モナドでExceptT
または類似を含めることになります。そして、あなたはcreateTenant
とactivateTenant
に新しい種類を提供します:
createTenant :: NewTenant -> AppM Tenant
activateTenant :: TenantId -> AppM Tenant
action :: NewTenant -> AppM Tenant
action = activateTenant . view key <=< createTenant
あなたは(activateTenant
用)ExceptT
(createTenant
用)とlift
で新しいモナドスタックに古い関数を変換することができます。何らかの理由でこのアプローチができない場合
、その後、あなたの代わりにあなたのコードが適切に読めなく作ることができます:
action = createTenant >=> either (return . Left) (\y -> Right <$> activateTenant (y ^. key))
OneをAppM
モナドでExceptT
を置くことの欠点は、あなたが区別する方法がありませんということです失敗することができるアクションとできないアクション。これがあなたにとって重要なら、あなたはカップルの選択肢があります。
ExcepT
をそのインスタンスのためだけに使用します。あなたはそのままであるAppM
を維持し、createTenant
とactivateTenant
の種類として-ですが、
action newTenant = runExceptT $ do
y <- ExcepT (createTenant newTenant)
lift (activateTenant (y ^. key))
またはその一行相当を記述します。
action n = runExcepT (ExceptT (createTenant n) >>= lift . activateTenant . view key)
その効果以上のあなたの行動が多型を確認します。あなたはまだAppM
モナドでExceptT
が含まれるであろうが、createTenant
とactivateTenant
の種類は、今あなたが、その後特にaction
に単相タイプAppM Tenant
を与えることができるだろう
createTenant :: (MonadReader AppConfig m, MonadIO m, MonadThrow TenantCreationError m)
=> NewTenant -> m Tenant
activateTenant :: (MonadReader AppConfig m, MonadIO m)
=> TenantId -> m Tenant
action :: (MonadReader AppConfig m, MonadIO m, MonadThrow TenantCreationError m)
=> NewTenant => m Tenant
action = activateTenant . view key <=< createTenant
だろう。 activateTenant
のタイプからは、それが失敗しないことは明らかである。さらに、それはあなたが以前には言えなかったことを言う機会を与えるだろう。例えばnewTenant
の場合は、IO
を実行する必要はありません。そのタイプの制約からMonadIO m
を削除することで指定できます。最も頻繁に使用されると予想される組み合わせのタイプシノニムを定義することによって、短いタイプのシグネチャを回復することができます。 (から[ `Data.Bifunctor`(https://hackage.haskell.org/package/bifunctors-3.2.0.1/docs/Data-Bifunctor.html)を使用してのコストで
type ConfigIO m = (MonadReader AppConfig m, MonadIO m)
type Failable m = (ConfigIO m, MonadThrow TenantCreationError m)
createTenant :: Failable m => NewTenant -> m Tenant
activateTenant :: ConfigIO m => TenantId -> m Tenant
'bifunctors')を使うと、' bimap'を使ってすぐに 'Either'の両側にマップすることができます。これは、 'createTenant newTenant >> = bimap pure(\ y - > activateTenant(y ^。key))'であなたを残します(もちろん、最後のラムダをポイントフリーにすることもできます。 : 'createTenant newTenant >> =バイマップ純粋(activateTenant。(^。key))')。 – Alec
@Alec Naively、それは型チェックのようには見えません。 'bimap'は内部のいくつかの' AppM'で 'Either'を返しません。あなたが望むものが' Either'を内部に持つ 'AppM'でしょうか? –
ああ、そうです。だからあなたはそうです。エドワード・クメットのパッケージには、このための['uncozipL'](http://hackage.haskell.org/package/adjunctions-4.3/docs/Data-Functor-Adjunction.html#v:uncozipL)があります。:) – Alec