web-dev-qa-db-ja.com

インターフェイスに代わる関数型プログラミングは何ですか?

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

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);

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

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

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

16
Ðаn

関数型プログラミングを命令型プログラミングよりも単純なものとして扱わないでください。構文の違いだけではありません。

この場合、オブジェクトの一意性を意味するGetIDメソッドがあります。これは、関数型プログラムを作成するための良い方法ではありません。おそらく、解決しようとしている問題を教えていただければ、より有意義なアドバイスを提供できるでしょう。

6
dan_waterworth

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

11
Jimmy Hoffa

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

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

これにより、関数は任意の型に作用します。

--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つのメカニズムの間で、タイプに関するあらゆる種類の複雑で興味深い動作を表現できます。

5
Daniel Gratzer

基本的な経験則は、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つは、ディクショナリは値ではなくタイプに関連付けられていることです(ディクショナリやOOバージョンで発生することなど)。これは、タイプがシステムでは、「タイプ」を混合することはできません[1]。「ブラガブル」のリストまたはブラジャー可能にするバイナリ関数が必要な場合、タイプクラスはすべてを同じタイプに制約しますが、ディクショナリーアプローチではブラジャー可能です。異なる起源(どのバージョンがより良いかは、あなたがしていることに大きく依存します)

[1]「既存の型」を行うための高度な方法がありますが、通常は問題になりません。

1
hugomg

言語固有になると思います。私は整然とした背景から来ています。多くの場合、状態とのインターフェースは機能モデルをある程度壊します。たとえば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)))
0
ipaul