JavaScriptモナドがシンプルに

スモークアートキューブからスモーク— MattysFlicks —(CC BY 2.0)
注:これは、JavaScript ES6 +で関数型プログラミングと合成ソフトウェア技術を一から学ぶ「ソフトウェアの作成」シリーズ(現在は本です!)の一部です。乞うご期待。まだまだたくさんあります!
<前へ| <<パート1からやり直す

モナドを学び始める前に、あなたはすでに知っているべきです:

  • 関数の構成:compose(f、g)(x)=(f∘g)(x)= f(g(x))
  • ファンクターの基本:Array.map()操作の理解。
「モナドを理解すると、すぐに他の人に説明することができなくなります」モナドグリーンの呪い〜ギラッドブラチャ(ダグラスクロックフォードが有名に使用)
「博士。 Hoenikkerは、自分が何をしていたかを8歳の子供に説明できなかった科学者は誰もがキャラタンだと言っていました。」〜Kurt Vonnegutの小説Cat’s Cradle

インターネットで「モナド」を検索すると、侵入不可能なカテゴリー理論の数学と、ブリトーと宇宙服の観点からモナドを「助けて」説明する多くの人々に襲われます。

モナドは単純です。専門用語は難しいです。本質を切り取りましょう。

モナドは、計算、分岐、I / Oなど、戻り値に加えてコンテキストを必要とする関数を構成する方法です。モナドはリフト、フラット化、およびマップをタイプするため、タイプはリフト関数a => M(b)のために整列し、構成可能になります。これは、リフト、フラット化、マップの実装の詳細に隠された、何らかの計算コンテキストに沿った、あるタイプaからあるタイプbへのマッピングです。

  • 関数マップ:a => b
  • ファンクターはコンテキストでマップします:Functor(a)=> Functor(b)
  • モナドはフラット化され、コンテキストでマップされます:Monad(Monad(a))=> Monad(b)

しかし、「フラット化」と「マップ」と「コンテキスト」はどういう意味ですか?

  • Mapは、「aに関数を適用し、a bを返す」ことを意味します。入力があれば、出力を返します。
  • コンテキストは、モナドの構成の計算の詳細です(リフト、フラット化、マップを含む)。 Functor / Monad APIとその機能は、アプリケーションの残りの部分でモナドを作成できるコンテキストを提供します。ファンクタとモナドのポイントは、そのコンテキストを抽象化することです。そうすることで、作成中に心配する必要がなくなります。コンテキスト内のマッピングは、a => bの関数をコンテキスト内の値に適用し、同じ種類のコンテキスト内にラップされた新しい値bを返すことを意味します。左側のオブザーバブル?右側のオブザーバブル:Observable(a)=> Observable(b)。左側の配列?右側の配列:Array(a)=> Array(b)。
  • 型リフトは、型をコンテキストに持ち上げ、その値からの計算、コンテキスト計算のトリガーなどに使用できるAPIで値を祝福することを意味します…a => F(a)(モナドは一種のファンクターです)。
  • Flattenは、コンテキストから値をアンラップすることを意味します。 F(a)=> a。

例:

const x = 20; //タイプが「a」のデータ
const f = n => n * 2; //「a」から「b」への関数
const arr = Array.of(x); //タイプリフト。
// JSには配列用の型リフトシュガーがあります:[x]
// .map()は、関数fを値xに適用します
//配列のコンテキスト内。
const result = arr.map(f); // [40]

この場合、Arrayはコンテキストであり、xはマッピングする値です。

この例には配列の配列は含まれていませんが、.concat()を使用してJSで配列をフラット化できます。

[] .concat.apply([]、[[1]、[2、3]、[4]]); // [1、2、3、4]

おそらくすでにモナドを使用しているでしょう。

スキルレベルやカテゴリ理論の理解に関係なく、モナドを使用するとコードの操作が簡単になります。モナドを利用しないと、コードの操作が難しくなる場合があります(たとえば、コールバック地獄、ネストされた条件分岐、より冗長)。

ソフトウェア開発の本質は作曲であり、モナドは作曲を容易にすることを忘れないでください。モナドとは何かの本質をもう一度見てみましょう。

  • 関数マップ:a => b。これにより、タイプa => bの関数を作成できます。
  • ファンクターはコンテキスト付きでマップします:Functor(a)=> Functor(b)。これにより、関数F(a)=> F(b)を構成できます。
  • モナドはフラット化され、コンテキストでマップされます:Monad(Monad(a))=> Monad(b)。リフティング関数a => F(b)を構成できます。

これらはすべて、関数構成を表現する単なる異なる方法です。関数が存在する理由は、それらを構成できるためです。関数を使用すると、複雑な問題を簡単に分離して簡単に解決できるため、さまざまな方法で構成してアプリケーションを作成できます。

関数とその適切な使用を理解するための鍵は、関数の構成をより深く理解することです。

関数構成は、データが通過する関数パイプラインを作成します。パイプラインの最初の段階で入力を行い、パイプラインの最後の段階からいくつかのデータをポップし、変換します。しかし、それが機能するためには、パイプラインの各ステージは、前のステージが返すデータ型を予期している必要があります。

タイプはすべて簡単に整列するため、単純な関数の作成は簡単です。出力タイプbと入力タイプbを一致させるだけで、ビジネスを開始できます。

g:a => b
f:b => c
h = f(g(a)):a => c

F(a)=> F(b)をマッピングしている場合、型が並んでいるので、ファンクターを使った作成も簡単です。

g:F(a)=> F(b)
f:F(b)=> F(c)
h = f(g(Fa)):F(a)=> F(c)

ただし、a => F(b)、b => F(c)などから関数を作成する場合は、モナドが必要です。それを明確にするために、F()をM()に交換しましょう。

g:a => M(b)
f:b => M(c)
h = composeM(f、g):a => M(c)

おっとっと。この例では、コンポーネントの関数タイプは揃っていません! fの入力には、タイプbが必要でしたが、得られたのはタイプM(b)(bのモナド)でした。その不整合のため、composeM()は、gが返すM(b)をアンラップして、fに渡すことができるようにする必要があります。fは、タイプM(b)ではなくタイプbを想定しているためです。このプロセス(多くの場合、.bind()または.chain()と呼ばれます)は、フラット化とマップが行われる場所です。

次の関数に渡す前に、M(b)からbをアンラップします。

g:a => M(b)== bに平坦化
f:bは=> M(c)にマッピングされます
h composeM(f、g):
               a flatten(M(b))=> b => map(b => M(c))=> M(c)

モナドはリフティング関数a => M(b)のために型を揃えて、それらを構成できるようにします。

上記の図では、M(b)=> bからのフラット化とb => M(c)からのマップは、a => M(c)からのチェーン内で発生します。チェーンの呼び出しはcomposeM()内で処理されます。高いレベルでは、心配する必要はありません。通常の関数の作成に使用するのと同じ種類のAPIを使用して、モナドを返す関数を作成するだけです。

多くの関数はa => bからの単純なマッピングではないため、モナドが必要です。一部の関数では、副作用(約束、ストリーム)の処理、分岐の処理(たぶん)、例外の処理(どちらか)などが必要です。

より具体的な例を次に示します。非同期APIからユーザーを取得し、そのユーザーデータを別の非同期APIに渡して計算を実行する必要がある場合はどうなりますか?

getUserById(id:String)=> Promise(User)
hasPermision(User)=> Promise(Boolean)

問題を実証するためにいくつかの関数を書きましょう。まず、ユーティリティ、compose()およびtrace():

const compose =(... fns)=> x => fns.reduceRight((y、f)=> f(y)、x);
const trace = label => value => {
  console.log( `$ {label}:$ {value}`);
  戻り値;
};

次に、作成するいくつかの関数:

{
  constラベル= 'API呼び出し構成';
  // a => Promise(b)
  const getUserById = id => id === 3?
    Promise.resolve({name: 'Kurt'、role: 'Author'}):
    未定義
  ;
  // b => Promise(c)
  const hasPermission =({ロール})=>(
    Promise.resolve(role === 'Author')
  );
  //それらを作成してみてください。警告:これは失敗します。
  const authUser = compose(hasPermission、getUserById);
  // おっとっと!常に偽!
  authUser(3).then(trace(label));
}

hasPermission()をgetUserById()で作成してauthUser()を作成しようとすると、hasPermission()がUserオブジェクトを予期し、代わりにPromise(User)を取得するため、大きな問題が発生します。これを修正するには、compose()をcomposePromises()に置き換える必要があります。これは、.then()を使用して関数の合成を行う必要があることを認識しているcomposeの特別なバージョンです。

{
  const composeM = chainMethod =>(... ms)=>(
    ms.reduce((f、g)=> x => g(x)[chainMethod](f))
  );
  const composePromises = composeM( 'then');
  constラベル= 'API呼び出し構成';
  // a => Promise(b)
  const getUserById = id => id === 3?
    Promise.resolve({name: 'Kurt'、role: 'Author'}):
    未定義
  ;
  // b => Promise(c)
  const hasPermission =({ロール})=>(
    Promise.resolve(role === 'Author')
  );
  //関数を作成します(これは動作します!)
  const authUser = composePromises(hasPermission、getUserById);
  authUser(3).then(trace(label)); // true
}

composeM()の動作については後で説明します。

モナドの本質を思い出してください:

  • 関数マップ:a => b
  • ファンクターはコンテキストでマップします:Functor(a)=> Functor(b)
  • モナドはフラット化され、コンテキストでマップされます:Monad(Monad(a))=> Monad(b)

この場合、モナドは本当にプロミスなので、これらのプロミスを返す関数を構成するとき、hasPermission()が期待しているUserの代わりにPromise(User)があります。 Monad(Monad(a))の外側のMonad()ラッパーを取り除いた場合、Monad(a)=> Monad(b)が残ることに注意してください。これは通常のファンクター.map()です。 Monad(x)=> xを平坦化できるものがあれば、ビジネスになります。

モナドは何でできているのか

モナドは単純な対称性に基づいています—値をコンテキストにラップする方法、およびコンテキストから値をアンラップする方法:

  • リフト/ユニット:あるタイプからモナドコンテキストへのタイプリフト:a => M(a)
  • 平坦化/結合:コンテキストから型をアンラップ:M(a)=> a

また、モナドはファンクターでもあるため、次のようにマッピングすることもできます。

  • マップ:コンテキストを保持したマップ:M(a)-> M(b)

mapとflattenを組み合わせると、チェーンが得られます。これは、ハインリッヒクライスリにちなんで名付けられたモナドリフティング関数の関数合成(別名Kleisli合成)です。

  • フラットマップ/チェーン:フラット化+マップ:M(M(a))=> M(b)

モナドの場合、.map()メソッドは多くの場合パブリックAPIから省略されます。 Lift + flattenは.map()を明確に記述しませんが、それを作成するために必要なすべての要素があります。 (of of / unit)とchain(bind / flatMap)を持ち上げることができる場合、.map()を作成できます。

const MyMonad = value =>({
  // <...任意のチェーンを挿入し、ここに...>
  map(f){
    return this.chain(a => this.constructor.of(f(a)));
  }
});

したがって、モナドに対して.of()および.chain()/。join()を定義すると、.map()の定義を推測できます。

リフトはfactory / constructorおよび/またはconstructor.of()メソッドです。カテゴリー理論では、「ユニット」と呼ばれます。それは、型をモナドのコンテキストに持ち上げるだけです。 aをaのモナドに変換します。

Haskellでは、(非常に紛らわしいことに)returnと呼ばれます。ほとんどの人が関数returnと混同するため、大声で話そうとすると非常に混乱します。私はほとんどの場合、散文では「lift」または「type lift」、コードでは.of()と呼びます。

その平坦化プロセス(.chain()のマップなし)は、通常flatten()またはjoin()と呼ばれます。多くの場合(常にではありません)、flatten()/ join()は.chain()/。flatMap()に組み込まれているため、完全に省略されます。フラット化はコンポジションに関連付けられることが多いため、マッピングと頻繁に組み合わされます。 => M(a)関数を構成するには、アンラップ+マップの両方が必要であることを忘れないでください。

扱うモナドの種類によっては、展開プロセスは非常に簡単です。アイデンティティモナドの場合、結果の値をモナドコンテキストに戻さないことを除いて、.map()と同じです。これには、1層のラッピングを破棄する効果があります。

{//アイデンティティモナド
const Id = value =>({
  //ファンクターマッピング
  // .map()のラッピングを保持する
  //マッピングされた値を型に渡します
  //持ち上げる:
  マップ:f => Id.of(f(value))、
  //モナド連鎖
  // 1レベルのラッピングを破棄します
  // .of()型リフトを省略することにより:
  チェーン:f => f(value)、
  //検査する便利な方法
  // その価値:
  toString:()=> `Id($ {value})`
});
//このモナドの型リフトはただ
//ファクトリへの参照。
Id.of = Id;

しかし、アンラップ部分は、副作用、エラー分岐、非同期I / Oの待機などの奇妙なものが通常隠れている場所でもあります。すべてのソフトウェア開発において、作曲は、本当に興味深いすべてのことが起こる場所です。

たとえば、promiseでは、.chain()は.then()と呼ばれます。 promise.then(f)を呼び出しても、f()はすぐには呼び出されません。代わりに、Promiseが解決するのを待ってから、f()を呼び出します(そのため名前が付けられます)。

例:

{
  const x = 20; // 値
  const p = Promise.resolve(x); //コンテキスト
  const f = n =>
    Promise.resolve(n * 2); // 関数
  const result = p.then(f); //アプリケーション
  result.then(
    r => console.log(r)// 40
  );
}

promiseでは、.chain()の代わりに.then()が使用されますが、ほぼ同じです。

約束は厳密にはモナドではないことを聞いたことがあるかもしれません。それは、値がそもそも約束である場合にのみ外側の約束を解くからです。それ以外の場合、.then()は.map()のように動作します。

ただし、promise値と他の値では動作が異なるため、.then()は、すべてのファンクターやモナドがすべての与えられた値を満たす必要のあるすべての数学的な法則を厳密には守りません。実際には、その動作の分岐を認識している限り、通常はどちらかとして扱うことができます。いくつかの一般的な構成ツールは、promiseでは期待どおりに動作しない可能性があることに注意してください。

モナド(別名Kleisli)構成の構築

約束を持ち上げる関数を作成するために使用したcomposeM関数を詳しく見てみましょう。

const composeM = method =>(... ms)=>(
  ms.reduce((f、g)=> x => g(x)[method](f))
);

その奇妙なレデューサーに隠されているのは、関数合成の代数的定義です:f(g(x))。見つけやすくします:

{
  //関数構成の代数的定義:
  //(f∘g)(x)= f(g(x))
  const compose =(f、g)=> x => f(g(x));
  const x = 20; // 値
  const arr = [x]; //コンテナ
  //作成するいくつかの関数
  const g = n => n + 1;
  const f = n => n * 2;
  // .map()が関数構成を達成することの証明。
  // mapへの呼び出しの連鎖は関数合成です。
  trace( 'map composes')([
    arr.map(g).map(f)、
    arr.map(compose(f、g))
  ]);
  // => [42]、[42]
}

これが意味することは、.map()メソッド(配列など)を提供するすべてのファンクタで機能する一般化された構成ユーティリティを書くことができるということです:

const composeMap =(... ms)=>(
  ms.reduce((f、g)=> x => g(x).map(f))
);

これは、標準f(g(x))のわずかな再定式化です。タイプa-> Functor(b)の任意の数の関数を指定すると、各関数を反復処理し、各関数をその入力値xに適用します。 .reduce()メソッドは、2つの入力値を持つ関数を取ります。アキュムレーター(この場合はf)、および配列内の現在の項目(g)です。

次のアプリケーションでfになる新しい関数x => g(x).map(f)を返します。上記で、x => g(x).map(f)がcompose(f、g)(x)をファンクターのコンテキストに持ち上げることと同等であることをすでに証明しました。つまり、コンテナ内の値にf(g(x))を適用することと同じです。この場合、配列内の値に構成を適用します。

パフォーマンスの警告:アレイにはこれをお勧めしません。この方法で関数を構成するには、配列全体(数十万のアイテムを含む可能性がある)で複数回の反復が必要です。配列上のマップの場合、単純なa-> b関数を最初に作成し、次に配列上に1回マッピングするか、.reduce()またはトランスデューサーで反復を最適化します。

配列データを介した同期的な熱心な関数アプリケーションの場合、これは過剰です。ただし、多くのことは非同期または遅延であり、多くの関数は、例外や空の値の分岐などの厄介なものを処理する必要があります。

そこでモナドが登場します。モナドは、コンポジションチェーン内の以前の非同期または分岐アクションに依存する値に依存できます。これらの場合、単純な関数構成に対して単純な値を取得することはできません。モナドを返すアクションは、a => bではなく、a => Monad(b)の形式を取ります。

データを受け取り、APIをヒットし、対応する値を返す関数と、そのデータを受け取り、別のAPIをヒットし、そのデータの計算結果を返す別の関数があるときはいつでも、関数を作成する必要があります。タイプa => Monad(b)の。 API呼び出しは非同期であるため、戻り値をpromiseやobservableのようなものでラップする必要があります。つまり、これらの関数のシグネチャは、それぞれa-> Monad(b)およびb-> Monad(c)です。

タイプg:a-> b、f:b-> cの関数の構成は、タイプが揃っているため簡単です。h:a-> cは、a => f(g(a))です。

タイプgの関数の作成:a-> Monad(b)、f:b-> Monad(c)は少し難しいです:h:a-> Monad(c)は単なるa => f(g(a))ではありませんfはMonad(b)ではなくbを期待しているためです。

もう少し具体的に見て、それぞれが約束を返す非同期関数のペアを作成しましょう。

{
  const label = 'Promise composition';
  const g = n => Promise.resolve(n + 1);
  const f = n => Promise.resolve(n * 2);
  const h = composePromises(f、g);
  h(20)
    .then(trace(label))
  ;
  //構成の約束:42
}

結果が正しく記録されるようにcomposePromises()を記述する方法は?ヒント:すでに見ました。

composeMap()関数を覚えていますか?必要なことは、.map()呼び出しを.then()に変更することだけです。 Promise.then()は基本的に非同期の.map()です。

{
  const composePromises =(... ms)=>(
    ms.reduce((f、g)=> x => g(x).then(f))
  );
  const label = 'Promise composition';
  const g = n => Promise.resolve(n + 1);
  const f = n => Promise.resolve(n * 2);
  const h = composePromises(f、g);
  h(20)
    .then(trace(label))
  ;
  //構成の約束:42
}

奇妙な部分は、2番目の関数f(gの後のfを思い出してください)を押すと、入力値が約束であることです。 b型ではなく、Promise(b)型ですが、fはラップされていないb型を取ります。どうしたの?

.then()の中には、Promise(b)-> bから展開するプロセスがあります。その操作は、結合またはフラット化と呼ばれます。

composeMap()とcomposePromises()がほぼ同一の関数であることに気づいたかもしれません。これは、両方を処理できる高階関数の完璧なユースケースです。 chainメソッドをカリー化された関数に混ぜて、角括弧表記を使用してみましょう。

const composeM = method =>(... ms)=>(
  ms.reduce((f、g)=> x => g(x)[method](f))
);

これで、次のような特殊な実装を作成できます。

const composePromises = composeM( 'then');
const composeMap = composeM( 'map');
const composeFlatMap = composeM( 'flatMap');

モナドの法則

独自のモナドの構築を開始する前に、すべてのモナドが満たすべき3つの法則があることを知っておく必要があります。

  1. 左のアイデンティティ:unit(x).chain(f)==== f(x)
  2. 正しいアイデンティティ:m.chain(unit)==== m
  3. 結合性:m.chain(f).chain(g)==== m.chain(x => f(x).chain(g))

アイデンティティ法

左右のアイデンティティ

モナドはファンクターです。ファンクターは、カテゴリ間の射であり、A-> Bです。射は矢印で表されます。オブジェクト間に明示的に表示される矢印に加えて、カテゴリ内の各オブジェクトには、それ自体に戻る矢印もあります。つまり、カテゴリ内のすべてのオブジェクトXに対して、矢印X-> Xが存在します。この矢印は、アイデンティティ矢印と呼ばれ、通常、オブジェクトを指し、同じオブジェクトにループバックする小さな円形の矢印として描画されます。 。

恒等射

連想性

連想性とは、作曲時に括弧をどこに置くかが問題ではないことを意味します。たとえば、追加する場合、a +(b + c)は(a + b)+ cと同じです。同じことが関数合成にも当てはまります:(f∘g)∘h = f∘(g∘h)。

Kleisliの構成にも同じことが当てはまります。後方に読むだけです。合成演算子(チェーン)が表示されたら、次のことを考えてください。

h(x).chain(x => g(x).chain(f))====(h(x).chain(g))。chain(f)

モナドの法則の証明

アイデンティティモナドがモナドの法則を満たしていることを証明しましょう:

{//アイデンティティモナド
  const Id = value =>({
    //ファンクターマッピング
    // .map()のラッピングを保持する
    //マッピングされた値を型に渡します
    //持ち上げる:
    マップ:f => Id.of(f(value))、
    //モナド連鎖
    // 1レベルのラッピングを破棄します
    // .of()型リフトを省略することにより:
    チェーン:f => f(value)、
    //検査する便利な方法
    // その価値:
    toString:()=> `Id($ {value})`
  });
  //このモナドの型リフトはただ
  //ファクトリへの参照。
  Id.of = Id;
  const g = n => Id(n + 1);
  const f = n => Id(n * 2);
  //左のアイデンティティ
  // unit(x).chain(f)==== f(x)
  trace( 'Id monad left identity')([
    Id(x).chain(f)、
    f(x)
  ]);
  // Idモナドはアイデンティティを残しました:Id(40)、Id(40)
  //正しいアイデンティティ
  // m.chain(unit)==== m
  trace( 'Id monad right identity')([
    Id(x).chain(Id.of)、
    Id(x)
  ]);
  // Idモナドの正体:Id(20)、Id(20)
  //結合性
  // m.chain(f).chain(g)====
  // m.chain(x => f(x).chain(g)
  trace( 'Idモナド結合性')([
    Id(x).chain(g).chain(f)、
    Id(x).chain(x => g(x).chain(f))
  ]);
  // Idモナド結合性:Id(42)、Id(42)
}

結論

モナドは、型リフティング関数を作成する方法です:g:a => M(b)、f:b => M(c)。これを達成するために、モナドはf()を適用する前にM(b)をbに平坦化する必要があります。言い換えると、ファンクターは、マップできるものです。モナドは、flatMapでできることです:

  • 関数マップ:a => b
  • ファンクターはコンテキストでマップします:Functor(a)=> Functor(b)
  • モナドはフラット化され、コンテキストでマップされます:Monad(Monad(a))=> Monad(b)

モナドは単純な対称性に基づいています—値をコンテキストにラップする方法、およびコンテキストから値をアンラップする方法:

  • リフト/ユニット:あるタイプからモナドコンテキストへのタイプリフト:a => M(a)
  • 平坦化/結合:コンテキストから型をアンラップ:M(a)=> a

また、モナドはファンクターでもあるため、次のようにマッピングすることもできます。

  • マップ:コンテキストを保持したマップ:M(a)-> M(b)

flattenをマップと組み合わせると、チェーンを取得します—関数を持ち上げるための関数合成、別名Kleisli合成:

  • FlatMap / Chain Flatten +マップ:M(M(a))=> M(b)

モナドは、総称してモナド則として知られる3つの法則(軸)を満たさなければなりません。

  • 左のアイデンティティ:unit(x).chain(f)==== f(x)
  • 正しいアイデンティティ:m.chain(unit)==== m
  • 結合性:m.chain(f).chain(g)==== m.chain(x => f(x).chain(g)

毎日JavaScriptコードで遭遇する可能性のあるモナドの例には、promiseとobservableが含まれます。 Kleisliコンポジションを使用すると、データタイプのAPIの詳細を気にせずに、データロジックをコンポーズできます。また、可能性のある副作用、条件分岐、またはchain()操作に隠されたアンラップ計算のその他の詳細を心配することなく、

これにより、モナドはコードを簡素化する非常に強力なツールになります。モナドが提供する単純化された利点を享受するために、モナドの内部で何が起こっているのかを理解したり心配する必要はありませんが、ボンネットの下を覗いてみるのはそれほど怖い見通しではありません。

モナドグリーン夫人の呪いを恐れる必要はありません。

ライブ1:1メンターシップでスキルをレベルアップ

DevAnywhereは、高度なJavaScriptスキルをレベルアップする最速の方法です。

  • ライブレッスン
  • 柔軟な時間
  • 1:1のメンターシップ
  • 実際の本番アプリを構築する
https://devanywhere.io/

エリックエリオットは「プログラミングJavaScriptアプリケーション」(O’Reilly)の著者であり、DevAnywhere.ioの共同設立者です。彼は、Adobe Systems、Zumba Fitness、The Wall Street Journal、ESPN、BBC、およびUsher、Frank Ocean、Metallicaなどのトップレコーディングアーティストのソフトウェアエクスペリエンスに貢献しています。

彼は、世界で最も美しい女性と、どこでも好きな場所で仕事をしています。