ホームページ バックエンド開発 Golang 機能パターン: インターフェイスとファンクター

機能パターン: インターフェイスとファンクター

Jul 18, 2024 pm 09:18 PM

これは、機能パターンというタイトルの一連の記事のパート 3 です。

残りの記事もぜひチェックしてください!

  1. モノイド
  2. 構成と暗黙性

ジェネリックスとタイプクラス

正しくするには、関数は型チェックを行う必要があり、したがって証明可能です。しかし、さまざまなタイプを処理することを目的とした一般化された関数の場合、これはすぐに問題点として現れます。 double 関数を複数の型にわたって機能させるには、それらを個別に定義する必要があります!

doubleInt :: Int -> Int
doubleChar :: Char -> Char
doubleFloat :: Float -> Float
-- ...
ログイン後にコピー

そして、自尊心のあるプログラマーであれば、これにはすでに完全に愕然としていると気づいているはずです。 部分的なアプリケーションを使用してケース処理を構築するためのパターンについて学んだところですが、型シグネチャではそれが許可されておらず、関数には 型チェックを行います。

ありがたいことに、これはほとんどの最新のプログラミング言語にすでに組み込まれている機能です。ジェネリック型を定義することができます。関数シグネチャまたは変数宣言内の一致する位置のみを検証する必要がある

仮説 型。

// c++
template <typename T>
T double(T x) {
    return x*2;
}
ログイン後にコピー
// rust
fn double<T>(x: T) -> T {
    return x*2;
}
ログイン後にコピー
-- haskell
double :: a -> a
double = (*2)       -- partially applied multiplication
ログイン後にコピー
それで問題は解決するはずです!コンパイラにこれらの

ジェネリックが与えられている限り、実行時にどの型を使用する必要があるかを判断できます(実際には、Rustはコンパイル時にこの推論を実行します!)。

ただし、この実装には利点がありますが、上記の Haskell コードでは実際にエラーが発生するため、実際には Haskell コンパイラによって指摘される明らかな欠陥がまだあります。

「*」の使用から生じる「Num a」のインスタンスはありません...

型を定義しましたが、この型が 2 倍になる

容量 を常に持っているとは限りません。確かに、これは数値に対してはすぐに機能しますが、ユーザーが文字列に対して double を呼び出すのを妨げているのは何でしょうか?リスト?これらの型を 2 倍にする事前定義された メソッド がなければ、そもそも引数として許可されるべきではありません。

したがって、

ジェネリックの名前に反して、もう少し具体的でありながらも一般的であるを取得する必要があります。

ここで

typeclasses が登場します。また、命令型の世界では一般に interface としても知られています。繰り返しになりますが、C++ より後に作成された言語を使用している場合は、インターフェイスの実装にアクセスできる必要があります。

インターフェースは、ジェネリックと比較して、その下で

分類できる型の何らかの機能を指​​定します。

これは、以前のコードの修正バージョンです。


double :: (Num a) => a -> a     -- a has to be of typeclass Num
double = (*2)
ログイン後にコピー
または Go:


// We first create an interface that is the union of floats and integers.
type Num interface {
    ~int | ~float64
    // ... plus all other num types
}

func double[T Num](a T) T {
    return a * 2
}
ログイン後にコピー
簡潔にするために、Haskell は Typescript や Go のインターフェース (純粋な関数規則によってもたらされる制約) などのインターフェースに埋め込まれた状態を実際には処理しないと言います。したがって、インターフェイスの下にある型の必須の

属性 を定義できる場合でも、純粋な インターフェイスでは 関数 または のみを定義する必要があることに注意してください。 >タイプの機能

このコンテキストでの機能とは、型に 2 倍関数の形で

依存関係 があるかどうかについて話しています。コンパイラーはそれを 2 倍にする方法を教えています?

import Control.Monad (join)

class CanDouble a where
  double :: a -> a

instance CanDouble Int where
  double = (* 2)

instance CanDouble Float where
  double = (* 2)

-- we tell the compiler that doubling a string is concatenating it to itself.
instance CanDouble String where 
  double = join (++)    -- W-combinator, f x = f(x)(x)
ログイン後にコピー
そして今、コードの繰り返しに関しては、ほぼ最初の状態に戻っています。面白いと思いませんか?

しかし、実際にこの機能が威力を発揮するのは、実装をきめ細かく制御することです。

戦略 パターンについて聞いたことがあるなら、機能的な意味では、これがまさにその通りです。

quadruple :: (CanDouble a) => a -> a
quadruple = double . double

leftShift :: (CanDouble a) => Int -> a -> a
leftShift n e
  | e <= 0 = n
  | otherwise = leftShift (double n) $ e - 1
ログイン後にコピー
これらの関数は型チェックを行うようになりました。これはすべて、CanDouble 型クラスで型を二重化する方法をコンパイラに

教えたためです。

Go でも同様のことを実現できますが、

非プリミティブ 型でのみインターフェイス メソッドを定義できるという大きな注意点があります。つまり、ラッパー構造体をプリミティブ型に定義する必要があります。

type CanDouble interface {
    double() CanDouble
}

type String string
type Number interface {
    ~int | ~float64
    // ... plus all other num types
}

type Num[T Number] struct {
    v T
}

func (s String) double() String {
    return s + s
}

func (n Num[T]) double() Num[T] {
    return Num[T]{n.v * 2}
}

func quadruple(n CanDouble) CanDouble {
    return n.double().double()
}

func leftShift(n CanDouble, e uint) CanDouble {
    for i := uint(0); i < e; i++ {
        n = n.double()
    }

    return n
}
ログイン後にコピー
これは正直言ってちょっと残念ですが、インターフェイスを扱うことになるほとんどの時間はカスタム型と構造体を扱うことになるので、心配する必要はありません。

カテゴリー

圏理論は、数学的構造とその関係の一般理論です。

私たちは The Monoid で

圏論 について簡単に取り上げましたが、接近遭遇のみで、そのままにしておきたいと思います。あちこちでこの内容を参照しますが、ご安心ください。以下の内容を理解するのに背景知識は必要ありません。

しかし、私たちが以前に

セットに遭遇したことがあるのは間違いありません。

簡単にまとめると、セットは要素の

コレクションと考えることができます。これらの要素は絶対に 何でも にすることができます。

{ 0, 1, 2, 3, ... }             -- the set of natural numbers
{ a, b, c, ..., z}              -- the set of lowercase letters
{ abs, min, max, ... }          -- the set of `Math` functions in Javascript
{ {0, 1}, {a, b}, {abs, min} }  -- the set of sets containing the first 2 elements of the above sets
ログイン後にコピー

Adding on to that, we have these things called morphisms, which we can think of a mapping between elements.

Very big omission here on the definitions of morphisms, in that they are relations between elements, and not strictly functions/mappings,
you can look it up if you are curious.

We can say a function like toUpper() is a morphism between lowercase letters to uppercase letters, just like how we can say double = (*2) is a morphism from numbers to numbers (specifically even numbers).

And if we group these together, the set of elements and their morphisms, we end up with a category.

Again, omission, categories have more constraints such as a Composition partial morphism and identities. But these properties are not that relevant here.

If you have a keen eye for patterns you'd see that there is a parallel to be drawn between categories and our interfaces! The objects (formal name for a category's set of elements) of our category are our instances, and our implementations are our morphisms!

class CanDouble a where
    double :: a -> a

-- `Int` is our set of elements { ... -1, 0, 1, ... }
-- `(* 2)` is a morphism we defined
-- ... (other omissions)
-- ...
-- Therefore, `CanDouble Int` is a Category.
instance CanDouble Int where
    double = (* 2)
ログイン後にコピー

Functors

Man, that was a lot to take in. Here's a little bit more extra:

A Functor is a type of a function (also known as a mapping) from category to another category (which can include itself, these are called endofunctors).

What this essentially means, is that it is a transformation on some category that maps every element to a corresponding element, and every morphism to a corresponding morphism. An output category based on the input category.

In Haskell, categories that can be transformed by a functor is described by the following typeclass (which also makes it a category in of itself, that's for you to ponder):

class Functor f where
    fmap :: (a -> b) -> f a -> f b
    -- ...
ログイン後にコピー

f here is what we call a type constructor. By itself it isn't a concrete type, until it is accompanied by a concrete type. An example of this would be how an array isn't a type, but an array of Int is. The most common form of a type constructor is as a data type (a struct).

From this definition we can surmise that all we need to give to this function fmap is a function (a -> b) (which is our actual functor, don't think about the naming too much), and this would transform a type f a to type f b, a different type in the same category.

Yes, this means Haskell's Functor typeclass is actually a definition for endofunctors, woops!

Functional Patterns: Interfaces and Functors

If all of that word vomit was scary, a very oversimplified version for the requirement of the Functor typeclass is that you are able to map values to other values in the same category.

Arguably the most common Functor we use are arrays:

instance Functor [] where
--  fmap f [] = []
--  fmap f (a:as) = f a : fmap as

    -- simplified
    fmap :: (a -> b) -> [a] -> [b]
    fmap f arr = map f arr
ログイン後にコピー

We are able to map an array of [a] to [b] using our function (or functor) f. The typeconstructor of [] serves as our category, and so our functor is a transformation from one type of an array to another.

So, formally: the map function, though commonly encountered nowadays in other languages and declarative frameworks such as React, is simply the application of an endofunctor on the category of arrays.

Wow. That is certainly a description.

Here are more examples of functors in action:

// Go
type Functor[T any] interface {
    fmap(func(T) T) Functor[T]
}

type Pair[T any] struct {
    a T
    b T
}

type List[T any] struct {
    get []T
}

// Applying a functor to a Pair is applying the function
// to both elements
func (p *Pair[T]) fmap(f func(T) T) Pair[T] {
    return Pair[T]{     // apply f to both a and b
        f(p.a),
        f(p.b),
    }
}

func (a *List[T]) fmap(f func(T) T) List[T] {
    res := make([]T, len(a.get))    // create an array of size len(a.get)

    for i, v := range a.get {
        res[i] = f(v)
    }

    return List[T]{res}
}
ログイン後にコピー
-- haskell
data Pair t = P (t, t)

instance Functor Pair where
    fmap f (P (x, y)) = P (f x, f y)
ログイン後にコピー

So all that it takes to fall under the Functor (again, endofunctor), interface is to have a definition on how to map the contents of the struct to any other type (including its own).

This is another simplifcation, functors also need to have property of identity and composition.

To put simply, whenever you do a map, you're not only transforming the elements of your array (or struct), you're also transforming the functions you are able to apply on this array (or struct). This is what we mean by mapping both objects and morphisms to different matching objects and morphisms in the same category.

This is important to note as even though we end up in the same category (in this context, we map an array, which results in another array), these might have differing functions or implementations available to them (though most of them will be mapped to their relatively equivalent functions, such as a reverse on an array of Int to reverse on an array of Float).

ここで、単純化しすぎてちょっと混乱してしまいます。定義だけに従えば、sum や concat などの還元関数は、配列のカテゴリからアトムへの関手であると言えますが、これは必ずしもそうではありません。真実。関手ではカテゴリー構造保存する必要もありますが、カテゴリー理論にあまりにも深く根ざしているため、この記事シリーズでは取り上げません。


この記事にアプリケーションよりもずっと多くの数学が含まれていた場合は申し訳ありませんが、これらの定義を理解することは、このシリーズの後半の難しいパターン、つまりアプリカティブと最後にモナドを理解するのに非常に役立ちます。

モナドは、エンドファンクターカテゴリ内のモノイドです。

もうすぐそこに到着します! :>

以上が機能パターン: インターフェイスとファンクターの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Golang vs. Python:パフォーマンスとスケーラビリティ Golang vs. Python:パフォーマンスとスケーラビリティ Apr 19, 2025 am 12:18 AM

Golangは、パフォーマンスとスケーラビリティの点でPythonよりも優れています。 1)Golangのコンピレーションタイプの特性と効率的な並行性モデルにより、高い並行性シナリオでうまく機能します。 2)Pythonは解釈された言語として、ゆっくりと実行されますが、Cythonなどのツールを介してパフォーマンスを最適化できます。

Golang and C:Concurrency vs. Raw Speed Golang and C:Concurrency vs. Raw Speed Apr 21, 2025 am 12:16 AM

Golangは並行性がCよりも優れていますが、Cは生の速度ではGolangよりも優れています。 1)Golangは、GoroutineとChannelを通じて効率的な並行性を達成します。これは、多数の同時タスクの処理に適しています。 2)Cコンパイラの最適化と標準ライブラリを介して、極端な最適化を必要とするアプリケーションに適したハードウェアに近い高性能を提供します。

ゴーを始めましょう:初心者のガイド ゴーを始めましょう:初心者のガイド Apr 26, 2025 am 12:21 AM

goisidealforforbeginnersandsutable forcloudnetworkservicesduetoitssimplicity、andconcurrencyfeatures.1)installgofromtheofficialwebsiteandverify with'goversion'.2)

Golang vs. C:パフォーマンスと速度の比較 Golang vs. C:パフォーマンスと速度の比較 Apr 21, 2025 am 12:13 AM

Golangは迅速な発展と同時シナリオに適しており、Cは極端なパフォーマンスと低レベルの制御が必要なシナリオに適しています。 1)Golangは、ごみ収集と並行機関のメカニズムを通じてパフォーマンスを向上させ、高配列Webサービス開発に適しています。 2)Cは、手動のメモリ管理とコンパイラの最適化を通じて究極のパフォーマンスを実現し、埋め込みシステム開発に適しています。

Golang vs. Python:重要な違​​いと類似点 Golang vs. Python:重要な違​​いと類似点 Apr 17, 2025 am 12:15 AM

GolangとPythonにはそれぞれ独自の利点があります。Golangは高性能と同時プログラミングに適していますが、PythonはデータサイエンスとWeb開発に適しています。 Golangは同時性モデルと効率的なパフォーマンスで知られていますが、Pythonは簡潔な構文とリッチライブラリエコシステムで知られています。

GolangとC:パフォーマンスのトレードオフ GolangとC:パフォーマンスのトレードオフ Apr 17, 2025 am 12:18 AM

GolangとCのパフォーマンスの違いは、主にメモリ管理、コンピレーションの最適化、ランタイム効率に反映されています。 1)Golangのゴミ収集メカニズムは便利ですが、パフォーマンスに影響を与える可能性があります。

パフォーマンスレース:ゴラン対c パフォーマンスレース:ゴラン対c Apr 16, 2025 am 12:07 AM

GolangとCにはそれぞれパフォーマンス競争において独自の利点があります。1)Golangは、高い並行性と迅速な発展に適しており、2)Cはより高いパフォーマンスと微細な制御を提供します。選択は、プロジェクトの要件とチームテクノロジースタックに基づいている必要があります。

Golang vs. Python:長所と短所 Golang vs. Python:長所と短所 Apr 21, 2025 am 12:17 AM

GolangisidealforBuildingsCalables Systemsduetoitsefficiency andConcurrency、Whilepythonexcelsinquickscriptinganddataanalysisduetoitssimplicityand vastecosystem.golang'ssignencouragesclean、readisinediteNeditinesinedinediseNabletinedinedinedisedisedioncourase

See all articles