2017-12-13 5 views
4

Option内の値を使用して別のTryを作成する関数を使用して、[Option [A]]をflatMapしたい単純で慣用的である。私はその問題を例で説明しました。目標は、3つの機能のいずれかからのエラーを含むことができる単一のTryでラップされたメンバーとイベントでOption [Group]を作成することです。Try [Option]を慣用的にflatMapするには

def getGroup(id: Long): Try[Option[Group]] 

def getMembersForGroup(groupId: Long): Try[Seq[Member]] 

def getMeetingsForGroup(groupId: Long): Try[Seq[Meeting]] 

私は「道で」オプションがありますので、試してみてくださいmember-、ミーティング-関数から試してみgetGroupで返されるからflatMapにそれが困難見つけます。これは私がこれまでに出ているものです:私は私の解決策については好きではない何

getGroup(id).flatMap(
    groupOpt => groupOpt.map(
    group => addStuff(group).map(group => Some(group)) 
).getOrElse(Success(None)) 
) 

def addStuff(g: Group): Try[Group] = 
    for { 
    members <- getMembersForGroup(g.id) 
    meetings <- getMeetingsForGroup(g.id) 
    } yield g.copy(members = members, meetings = meetings) 

は私がgetOrElseを実行するには、オプションでaddStuffによって返されたグループをラップしなければならないということです。この時点で、タイプは[Try [Option [Group]] [Option] [Try]となっています。

この問題の解決法は簡単ですか? addStuffコールからの潜在的な故障情報を無視しての費用であろうことが、

getGroup(id).map(
    groupOpt => groupOpt.flatMap(
     group => addStuff(group).toOption 
    ) 
) 

+0

はどのように3を組み合わせていますか?どのようなタイプですか?いくつかのResultTypeに '(Seq [Member]、Seq [Meeting]、Group)=> ResultType'関数があると仮定しますか? – stewSquared

+0

私はfor-comprehensionによって得られたコピー関数を使用します。結果のタイプをTry [Option [Group]]にしたい。 –

+0

この質問のタイトルを読んで、私はhttps://www.manning.com/books/functional-and-reactive-domain-modelingを読むことをお勧めします! –

答えて

0

はおそらくにあなたのgetGroupコールを簡素化することができます。受け入れられない場合は、コードをさらに単純化することはできません。

1

あなたが代わりに.map.getOrElseの​​を使用することができます...それはそれは少しよりよい作る:

getGroup(id) 
    .flatMap { 
    _.fold(Try(Option.empty[Group])){ 
     addStuff(_).map(Option.apply) 
    } 
    } 

または明示的に2例の書き込み - この場合は少し明確に見えるかもしれない、あなたが避けることができるので、醜い探して型シグネチャスペルアウトした:documentation heresource here

getGroup(id).flatMap { 
    case None => Success(None) 
    case Some(group) => addStuff(group).map(Option.apply) 
    } 
+0

Option.applyを取り除くことができればいいですが、これは本当に欲しいものですが、不可能かもしれません。しかし、flatMapとpatternMatchingを併用することは改善です。 –

+1

あなたは 'option'を' addStuff'にラップすることができます。私は推測します...ポイントは、タイプが異なるためどこかで発生しなければならないということです。私はこの種のことのために私のプロジェクトで暗黙の '.toOption'を作ったので、' yield g.copy(members = members、meetings = meetings).toOption'を 'addStuff'の最後の行ネストされたカッコは嫌いです:))。 – Dima

3

猫はこれを簡素化する可能性があるOptionTタイプがあります。

あなたの例では、次のようになります。

def getGroupWithStuff(id: Long): OptionT[Try, Group] = { 
    for { 
    g <- OptionT(getGroup(id)) 
    members <- OptionT.liftF(getMembersForGroup(g.id)) 
    meetings <- OptionT.liftF(getMeetingsForGroup(g.id)) 
    } yield g.copy(members = members, meetings = meetings) 
} 
+0

また、持ち上げるのではなく、メンバーと会議を追加するflatMapF。 – stewSquared

+1

これは最もクリーンなソリューションのように見えますが、OptionT全体のコンセプトを導入するとソリューションが複雑になると感じています。私はよりバニラのような解決策を望んでいると思う。 –

+0

私はそれを間違いなく理解することができます。 1つの小さな問題を解決するための新しいライブラリを導入する価値はありません。ちょうどもう1つのオプションを提供したいと思った。 –

0

はこれを試してみてください。あなたは、3つの呼び出し(いずれか最初に失敗したもの)のいずれかから、理解の構文と失敗情報を保持します。

def getFullGroup(id: Long): Try[Option[Group]] = 
    getGroup(id).flatMap[Option[Group]] { _.map[Try[Group]]{ group => 
    for { 
     meetings <- getMeetingsForGroup(id) 
     members <- getMembersForGroup 
    } yield group.copy(meetings = meetings, members = members) 
    }.fold[Try[Option[Group]]](Success(None))(_.map(Some(_))) 
} 

注終わり型アクロバット:

fold[Try[Option[Group]]](Success(None))(_.map(Some(_)))

これは、型注釈およびIDEなしで権利を取得するのは難しいです。この特別なケースではそれほど悪くはありませんが、ミーティングやメンバーが別のネストされたtryオプションに依存していて、それが元のものに依存していることを想像してください。リスト全体を使うのではなく、個々のミーティングとグループについて理解したいと思っているかどうか想像してみてください。

ネコでないOptionT[Try, Group]Try[Option[Group]]をスタックするには、猫やスカラのOptionTモナドトランスを使用してみてください。モナド変圧器を使用している場合は、次のようになります。

def getFullGroup(id: Long): OptionT[Try, Group] = 
    OptionT(getGroup(id)).flatMapF { group => 
    for { 
     meetings <- getMeetingsForGroup(id) 
     members <- getMembersForGroup(id) 
    } yield group.copy(meetings = meetings, members = members) 
    } 
} 

この場合、実際にはあまり効果がありません。しかし、この種のコードがたくさんある場合は、それを調べてください。

ところで、TryとOptionを反転させる最初の例の最後のボイラープレートは、sequenceと呼ばれています。地図をたどると、全体がtraverseと呼ばれます。これは、頻繁に出現し、関数型プログラミングライブラリによって抽象化されたパターンです。代わりにOptionTを使用して、あなたのような何かを行うことができます。

def getFullGroup(id: Long): Try[Option[Group]] = 
    getGroup(id).flatMap[Option[Group]] { _.traverse { group => 
    for { 
     meetings <- getMeetingsForGroup(id) 
     members <- getMembersForGroup 
    } yield group.copy(meetings = meetings, members = members) 
    } 
} 

を(あなたがモナドをひっくり返すfをマッピングしている場合は一般的に、あなたはfでトラバースします。)

関連する問題