2016-09-25 4 views
3

以下の関数を1行で書き換えることはできますか?ReaderTとどちらかで動作する関数をどのように構成するのですか?

action :: NewTenant -> AppM (Either TenantCreationError Tenant) 
action newTenant = (createTenant newTenant) >>= \case 
    Left x -> return $ Left x 
    Right y -> do 
    t <- activateTenant $ (y ^. key) 
    return $ Right t 

type AppM = ReaderT AppConfig IO 
createTenant :: NewTenant -> AppM (Either TenantCreationError Tenant) 
activateTenant :: TenantId -> AppM Tenant 
+0

'bifunctors')を使うと、' bimap'を使ってすぐに 'Either'の両側にマップすることができます。これは、 'createTenant newTenant >> = bimap pure(\ y - > activateTenant(y ^。key))'であなたを残します(もちろん、最後のラムダをポイントフリーにすることもできます。 : 'createTenant newTenant >> =バイマップ純粋(activateTenant。(^。key))')。 – Alec

+0

@Alec Naively、それは型チェックのようには見えません。 'bimap'は内部のいくつかの' AppM'で 'Either'を返しません。あなたが望むものが' Either'を内部に持つ 'AppM'でしょうか? –

+0

ああ、そうです。だからあなたはそうです。エドワード・クメットのパッケージには、このための['uncozipL'](http://hackage.haskell.org/package/adjunctions-4.3/docs/Data-Functor-Adjunction.html#v:uncozipL)があります。:) – Alec

答えて

3

おそらく最良の方法は、あなたのAppMモナドでExceptTまたは類似を含めることになります。そして、あなたはcreateTenantactivateTenantに新しい種類を提供します:

createTenant :: NewTenant -> AppM Tenant 
activateTenant :: TenantId -> AppM Tenant 

action :: NewTenant -> AppM Tenant 
action = activateTenant . view key <=< createTenant 

あなたは(activateTenant用)ExceptTcreateTenant用)とliftで新しいモナドスタックに古い関数を変換することができます。何らかの理由でこのアプローチができない場合

、その後、あなたの代わりにあなたのコードが適切に読めなく作ることができます:

action = createTenant >=> either (return . Left) (\y -> Right <$> activateTenant (y ^. key)) 

OneをAppMモナドでExceptTを置くことの欠点は、あなたが区別する方法がありませんということです失敗することができるアクションとできないアクション。これがあなたにとって重要なら、あなたはカップルの選択肢があります。

  1. ExcepTをそのインスタンスのためだけに使用します。あなたはそのままであるAppMを維持し、createTenantactivateTenantの種類として-ですが、

    action newTenant = runExceptT $ do 
        y <- ExcepT (createTenant newTenant) 
        lift (activateTenant (y ^. key)) 
    

    またはその一行相当を記述します。

    action n = runExcepT (ExceptT (createTenant n) >>= lift . activateTenant . view key) 
    
  2. その効果以上のあなたの行動が多型を確認します。あなたはまだAppMモナドでExceptTが含まれるであろうが、createTenantactivateTenantの種類は、今あなたが、その後特に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 
    
+0

だから、あなたは「どちらか」を一掃することを提案していますか? –

+0

@SaurabhNandaまったくありません! 'Either'は隠れているだけでなく、' ExceptT e m a'は 'm(either e)'と同型です。 'ExceptT ema'が' m(Either) 'よりも優れているのは、' Monad'インスタンスの '(>> =)'操作があなたのために 'Either'を破壊し再構築して処理し、 - 「左」が表示されたときの回路。 –

+0

しかし、私の 'AppM'モナドの' ExceptT'部分を作ると、 'Left'や' Right'を返すようにすべての関数が強制されませんか?実際にエラーケースに陥ることはありませんか? –

関連する問題