インターフェイスに代わる機能プログラミングとは何ですか?


15

「機能的な」スタイルでプログラミングしたい場合、インターフェイスを何に置き換えますか?

interface IFace
{
   string Name { get; set; }
   int Id { get; }
}
class Foo : IFace { ... }

たぶんTuple<>

Tuple<Func<string> /*get_Name*/, Action<String> /*set_Name*/, Func<int> /*get_Id*/> Foo;

そもそもインターフェイスを使用している唯一の理由は、特定のプロパティ/メソッドを常に利用できるようにしたいからです。


編集:私が考えている/試みていることについてのいくつかの詳細。

たとえば、次の3つの関数を取るメソッドがあります。

static class Blarf
{
   public static void DoSomething(Func<string> getName, Action<string> setName, Func<int> getId);
}

Bar私のインスタンスでこのメソッドを使用できます:

class Bar
{
   public string GetName();
   public void SetName(string value);

   public int GetId();
}
...
var bar = new Bar();
Blarf.DoSomething(bar.GetName, bar.SetName, bar.GetId);

しかしbar、1回の呼び出しで3回言及しなければならないので、それは少し苦痛です。さらに、呼び出し元が異なるインスタンスから関数を提供することを本当に意図していない

Blarf.DoSomething(bar1.GetName, bar2.SetName, bar3.GetId); // NO!

C#では、interfaceこれに対処する1つの方法です。しかし、それは非常にオブジェクト指向のアプローチのようです。もっと機能的な解決策があるかどうか疑問に思っています:1)関数のグループを一緒に渡し、2)関数が互いに適切に関連していることを確認します。


あなたはしません。データ型のインターフェイスはまったく問題ありません(ただし、不変オブジェクトを好むでしょう)。
テラスティン

1
SICPの第2章では、これについてほぼ説明しています。
user16764

7
あなたの質問を読み直した後、私はあなたが達成しようとしている特定の機能に興味がありますか?何を求めているように見えることは意味がない機能的なスタイルで、インスタンスに対してOOスタイルのサイド副作用のprogramminを行う方法..です
ジミー・ホッファ

答えは言語に依存します。Clojure ではclojure.org / protocolsを使用できます。ここで唯一のソフト領域は、関数が操作する必要のあるパラメーターの種類(オブジェクト)のみです。
ジョブ

1
シンプルにしましょう。これらのメソッドポインターを含む構造体と、オブジェクトインスタンスから初期化する関数です。Haskellを選ぶ理由 ;)
mlvljr

回答:


6

関数型プログラミングを命令型プログラミングよりも薄いベニアとして扱わないでください。単なる構文上の違い以上のものがあります。

この場合、GetIDオブジェクトの一意性を暗示するメソッドがあります。これは、機能的なプログラムを作成するための良い方法ではありません。おそらくあなたが解決しようとしている問題を私たちに伝えることができ、私たちはあなたにもっと有意義なアドバイスを与えることができます。


3
「ロシア語で考えなければならない。」
Ðаn

けっこうだ; 私の本当の問題は特に興味深いものではありません。私はすでにC#でうまく機能している(うまく動作していますコードを本当に見たい場合はgithub.com/JDanielSmith/Projects/tree/master/PictureOfTheDay)が、より機能的なスタイルで行うのは楽しいでしょうまだC#を使用しています。
Ðаn

1
@Ðаnそれは本当にFirefox(映画)の引用ですか(素晴らしいからですですか(もしそれがなら)。それとも他の場所で使用されていますか?
icc97

2
私が同意するように、それが命令型プログラミングから関数型プログラミングへの完全なパラダイムシフトである場合、命令型の方法で記述されたより多くの桁のコード行があります。そのため、巨大なシステム用に記述されたコーナーケースの多くは、命令型プログラミングで発見されています。命令型プログラミングには多くの優れた実践があり、それらのスキルを翻訳できるかどうか、またはFPの問題ではないかどうかを知ることは価値のある質問です。恐ろしいコードをFPで書くことは完全に可能であるため、この種の質問はFPの良い部分も強調するはずです。
icc97

11

Haskellとその派生クラスには、インターフェイスに似た型クラスがあります。カプセル化の方法について質問しているようですが、これは型システムに関する質問です。hindley Milner型システムは関数型言語では一般的であり、言語間でさまざまな方法でこれを行うデータ型があります。


5
タイプクラスの+1-HaskellタイプクラスとJavaインターフェースの主な違いは、両方が別々に宣言された、タイプクラスがタイプ関連付けられることです。古い「インターフェース」を使用して新しいタイプにアクセスするのと同じくらい簡単に、新しい「インターフェース」を介して古いタイプを使用できます。データを非表示にするには、モジュールのタイプの実装を非表示にします。少なくともEiffelの名声のBertrand Meyerによれば、OOPクラス一種のモジュールです。
Steve314

5

関数が複数の入力を処理できるようにする方法はいくつかあります。

最初で最も一般的なのは、パラメトリック多相性です。

これにより、関数は任意の型で動作できます。

--Haskell Example
id :: a -> a --Here 'a' is just some arbitrary type
id myRandomThing = myRandomThing

head :: [a] -> a
head (listItem:list) = listItem

これは素晴らしいことですが、OOインターフェースが持つ動的なディスパッチを提供しません。このため、Haskellには型クラスがあり、Scalaには暗黙のものがあります。

class Addable a where
   (<+>) :: a -> a -> a
instance Addable Int where
   a <+> b = a + b
instance Addable [a] where
   a <+> b = a ++ b

--Now we can get that do something similar to OO (kinda...)
addStuff :: (Addable a) => [a] -> a
-- Notice how we limit 'a' here to be something Addable
addStuff (x:[]) = x
addStuff (x:xs) = x <+> addStuff xs
-- In better Haskell form
addStuff' = foldl1 <+>

これらの2つのメカニズムの間で、あなたはあなたのタイプのあらゆる種類の複雑で興味深い行動を表現することができます。


1
回答の言語が質問の言語と一致しない場合は、構文の強調表示のヒントを追加できます。たとえば、私の提案する編集を参照してください。
hugomg

1

基本的な経験則は、FPプログラミング関数はオブジェクト指向プログラミングでオブジェクトが行うのと同じ仕事をするということです。それらのメソッド(とにかく "call"メソッド)を呼び出すことができ、カプセル化された内部ルールに従って応答します。特に、そこにあるすべての適切なFP言語では、クロージャー/レキシカルスコープを使用して、関数に「インスタンス変数」を含めることができます。

var make_OO_style_counter = function(){
   return {
      counter: 0
      increment: function(){
          this.counter += 1
          return this.counter;
      }
   }
};

var make_FP_style_counter = function(){
    var counter = 0;
    return fucntion(){
        counter += 1
        return counter;
    }
};

次の質問は、インターフェースとはどういう意味ですか?1つのアプローチは、名目上のインターフェイスを使用することです(そうだと言われた場合、インターフェイスに準拠します)-これは通常、使用している言語に大きく依存するため、そのままにしておきます。インターフェースを定義するもう1つの方法は、構造的な方法で、どのパラメーターが受信して返すかを確認します。これは、動的なアヒル型言語でよく見かける種類のインターフェースであり、すべてのFPに非常によく適合します。正しい型がインターフェースに適合します!

したがって、インターフェイスに一致するオブジェクトを表す最も簡単な方法は、単に関数のグループを作成することです。通常、何らかの種類のレコードに関数をパックすることで、関数を個別に渡すというさを回避できます。

var my_blarfable = {
 get_name: function(){ ... },
 set_name: function(){ ... },
 get_id:   function(){ ... }
}

do_something(my_blarfable)

ネイキッド関数または関数のレコードを使用すると、一般的な問題のほとんどを、大量のボイラープレートなしで「脂肪のない」方法で解決できます。それより高度なものが必要な場合は、言語によって追加の機能が提供されることがあります。人々が言及した1つの例は、Haskell型クラスです。型クラスは、本質的に、型を関数のそれらのレコードの1つと関連付け、辞書が暗黙的であり、必要に応じて内部関数に自動的に渡されるように記述できるようにします。

-- Explicit dictionary version
-- no setters because haskell doesn't like mutable state.
data BlargDict = BlargDict {
    blarg_name :: String,
    blarg_id   :: Integer
}

do_something :: BlargDict -> IO()
do_something blarg_dict = do
   print (blarg_name blarg_dict)
   print (blarg_id   blarg_dict)

-- Typeclass version   
class Blargable a where
   blag_name :: a -> String
   blag_id   :: a -> String

do_something :: Blargable a => a -> IO
do_something blarg = do
   print (blarg_name blarg)
   print (blarg_id   blarg)

ただし、タイプクラスについて注意すべき重要な点は、辞書が値ではなくタイプに関連付けられていることです(ディクショナリおよびオブジェクト指向バージョンで発生することなど)。つまり、型システムでは「型」を混在させることはできません[1]。「ブラジャーブル」のリストまたはブラジャーブルを取得するバイナリ関数が必要な場合、タイプクラスはすべてを同じタイプに制限しますが、辞書アプローチでは異なる起源のブラジャーブルを使用できます(どちらのバージョンがより良いかは、やる)

[1]「存在型」を実行するための高度な方法がありますが、通常は面倒な価値はありません。


0

言語固有のものになると思います。私は素朴な背景から来ています。多くの場合、状態とのインターフェースは機能モデルをある程度破壊します。そのため、たとえば、CLOSは、LISPの機能が低下し、命令型言語に近い場所です。一般的に、必要な関数パラメーターをより高いレベルのメソッドと組み合わせて、おそらく探しているものです。

;; returns a function of the type #'(lambda (x y z &optional a b c)

(defun get-higher-level-method-impl (some-type-of-qualifier) 
    (cond ((eq 'foo) #'the-foo-version)
          ((eq 'bar) #'the-bar-version)))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.