함수형 프로그래밍과 PLT 서클에서 "대수학"이라는 용어를 여러 번 들었습니다. 특히 토론이 객체, 코 모나드, 렌즈 등에 관한 것입니다. 이 용어를 인터넷으로 검색하면 이러한 구조에 대한 수학적 설명을 제공하는 페이지가 나와 있으며, 이는 거의 이해할 수없는 내용입니다. 프로그래밍 맥락에서 대수학이 무엇을 의미하는지, 그 의미가 무엇이며, 객체와 코모 나와 어떻게 관련되는지 설명해 주시겠습니까?
함수형 프로그래밍과 PLT 서클에서 "대수학"이라는 용어를 여러 번 들었습니다. 특히 토론이 객체, 코 모나드, 렌즈 등에 관한 것입니다. 이 용어를 인터넷으로 검색하면 이러한 구조에 대한 수학적 설명을 제공하는 페이지가 나와 있으며, 이는 거의 이해할 수없는 내용입니다. 프로그래밍 맥락에서 대수학이 무엇을 의미하는지, 그 의미가 무엇이며, 객체와 코모 나와 어떻게 관련되는지 설명해 주시겠습니까?
답변:
시작해야 할 곳은 대수학 의 개념을 이해하는 것이라고 생각합니다 . 이것은 그룹, 고리, 모노 이드 등과 같은 대수 구조의 일반화입니다. 대부분의 경우, 이러한 것들은 세트의 관점에서 소개되지만, 우리가 친구 사이이므로 Haskell 유형에 대해 이야기하겠습니다. (하지만 그리스 문자 사용을 거부 할 수는 없습니다. 모든 것이 더 시원해 보입니다!)
그러면 대수는 τ
일부 기능과 정체성을 가진 유형일뿐 입니다. 이 함수는 다른 수의 인수를 τ
사용하여 τ
: curried를 생성합니다 (τ, τ,…, τ) → τ
. 또한 τ
일부 기능에서 특별한 동작 을 하는 요소 인 "정체성"을 가질 수 있습니다 .
가장 간단한 예는 monoid입니다. monoid는 τ
함수 mappend ∷ (τ, τ) → τ
와 아이디를 가진 모든 유형 입니다 mzero ∷ τ
. 다른 예에는 그룹 (추가 invert ∷ τ → τ
기능을 제외하고는 모노 이드와 유사 함 ), 고리, 격자 등이 포함됩니다.
모든 기능은 작동 τ
하지만 다른 특성을 가질 수 있습니다. 로 우리는이를 쓸 수있는 τⁿ → τ
곳 τⁿ
의 튜플에 매핑 n
τ
. 이런 식으로, 정체성을 빈 터플이있는 τ⁰ → τ
곳 으로 생각하는 것이 합리적 입니다. 따라서 대수학의 개념을 실제로 단순화 할 수 있습니다. 이것은 몇 가지 함수가있는 유형일뿐입니다.τ⁰
()
대수는 코드에서하는 것처럼 "계산 된"수학의 일반적인 패턴입니다. 사람들은 앞서 언급 한 monoids, groups, lattices 등 흥미로운 것들이 모두 비슷한 패턴을 따른다는 것을 알아 차렸다. 이 작업의 장점은 프로그래밍과 동일합니다. 재사용 가능한 증명을 만들고 특정 종류의 추론을 더 쉽게 만듭니다.
그러나 우리는 팩토링을 완전히하지 않았습니다. 지금까지 많은 기능이 τⁿ → τ
있습니다. 우리는 실제로 그것들을 모두 하나의 함수로 결합하는 깔끔한 트릭을 할 수 있습니다. 특히, monoids에서 살펴 보자 : 우리가 가지고 mappend ∷ (τ, τ) → τ
와 mempty ∷ () → τ
. 합 유형을 사용하여 이러한 함수를 단일 함수로 변환 할 수 있습니다 Either
. 다음과 같이 보일 것입니다 :
op ∷ Monoid τ ⇒ Either (τ, τ) () → τ
op (Left (a, b)) = mappend (a, b)
op (Right ()) = mempty
우리는 실제로 결합 반복이 변환을 사용할 수 있습니다 모든τⁿ → τ
를 들어, 하나 하나에 기능을 어떤 대수. (사실, 우리는 기능의 수에 대해이 작업을 수행 할 수 있습니다 a → τ
, b → τ
대한상의 있도록 모든 a, b,…
.)
이것은 우리가 유형으로 대수에 대해 이야기 할 수 있습니다 τ
A를 하나 의 약간의 혼란에서 기능 Either
의 하나에 τ
. 모노 아이드의 경우이 혼란은 다음과 같습니다 Either (τ, τ) ()
.; (여분이있는 그룹에 대한 τ → τ
작업을)은 다음과 같습니다 Either (Either (τ, τ) τ) ()
. 각 구조마다 유형이 다릅니다. 그렇다면 이러한 모든 유형의 공통점은 무엇입니까? 가장 분명한 것은 그것들이 모두 대수의 데이터 타입이라는 단순한 제품이라는 것입니다. 예를 들어, monoid의 경우 monoid τ에서 작동하는 monoid 인수 유형을 만들 수 있습니다 .
data MonoidArgument τ = Mappend τ τ -- here τ τ is the same as (τ, τ)
| Mempty -- here we can just leave the () out
우리는 그룹과 고리, 격자 및 다른 모든 가능한 구조에 대해 같은 일을 할 수 있습니다.
이 모든 유형에서 특별한 점은 무엇입니까? 글쎄, 그들은 모두입니다 Functors
! 예 :
instance Functor MonoidArgument where
fmap f (Mappend τ τ) = Mappend (f τ) (f τ)
fmap f Mempty = Mempty
따라서 대수학에 대한 아이디어를 더 일반화 할 수 있습니다. functor 용 τ
함수가있는 유형 일뿐 입니다. 실제로 이것을 타입 클래스로 작성할 수 있습니다.f τ → τ
f
class Functor f ⇒ Algebra f τ where
op ∷ f τ → τ
이것은 functor에 의해 결정되기 때문에 종종 "F- 대수"라고합니다 F
. 타입 클래스를 부분적으로 적용 할 수 있다면 다음과 같이 정의 할 수 class Monoid = Algebra MonoidArgument
있습니다.
이제 대수가 무엇인지 그리고 그것이 정상적인 대수 구조를 일반화하는 방법을 잘 알고 있기를 바랍니다. F- 대수 란 무엇입니까? 공동은 그것이 대수의 "이중"이라는 것을 암시합니다. 즉, 우리는 대수를 취하고 약간의 화살표를 뒤집습니다. 위의 정의에는 하나의 화살표 만 표시되므로 간단히 뒤집어 보겠습니다.
class Functor f ⇒ CoAlgebra f τ where
coop ∷ τ → f τ
그리고 그게 다야! 이제이 결론은 약간의 플립 팬트처럼 보일 수 있습니다. 그것은 대수학이 무엇인지 알려주지 만, 그것이 어떻게 유용한 지 또는 왜 우리가 관심을 갖는지에 대한 통찰력을 제공하지는 않습니다. 좋은 예나 두 가지를 찾거나 생각해 내면 조금 더 이해하겠습니다. : P.
조금 읽은 후에는 클래스와 객체를 표현하기 위해 대수를 사용하는 방법에 대한 좋은 아이디어가 있다고 생각합니다. C
클래스에서 가능한 모든 객체의 내부 상태를 포함 하는 유형 이 있습니다. 클래스 자체는 C
객체의 메소드와 속성을 지정 하는 대수 입니다.
대수학의 예와 같이 우리가 같은 기능의 무리가있는 경우, a → τ
그리고 b → τ
어떤를 들어 a, b,…
, 우리는 모든 사용하여 하나의 함수로 결합 할 수 있습니다 Either
, 합계 유형을. 이중 "개념"유형의 기능에 무리를 결합하는 것 τ → a
, τ → b
등등. 합산 유형 (제품 유형)의 이중을 사용하여이 작업을 수행 할 수 있습니다. 위의 두 함수 ( f
및 g
)가 주어지면 다음 과 같이 하나의 함수 를 만들 수 있습니다.
both ∷ τ → (a, b)
both x = (f x, g x)
이 유형 (a, a)
은 간단한 방식으로 functor이므로 F-coalgebra라는 개념에 꼭 맞습니다. 이 특별한 트릭을 통해 여러 가지 함수 또는 OOP의 경우 메소드를 단일 함수 유형으로 패키지 할 수 있습니다 τ → f τ
.
우리 유형의 요소 는 객체 C
의 내부 상태를 나타냅니다 . 객체에 읽을 수있는 속성이있는 경우 상태에 따라 달라질 수 있어야합니다. 이 작업을 수행하는 가장 확실한 방법은 기능을로 만드는 것입니다 C
. 그래서 만약 우리가 length 속성을 원한다면 object.length
우리는 함수를 가질 것 C → Int
입니다.
인수를 취하고 상태를 수정할 수있는 메소드를 원합니다. 이를 위해서는 모든 인수를 취하고 새로운을 생성해야합니다 C
. 과 좌표 setPosition
를 취하는 방법을 상상해 봅시다 : . 다음과 같이 보일 것입니다 : .x
y
object.setPosition(1, 2)
C → ((Int, Int) → C)
여기서 중요한 패턴은 객체의 "방법"과 "속성"이 객체 자체를 첫 번째 인수로 취하는 것입니다. 이것은 self
파이썬 의 매개 변수와 같으며 this
다른 많은 언어 의 암시와 같습니다 . coalgebra는 기본적으로 단지 복용의 동작 캡슐화 self
매개 변수를 : 첫 번째 무슨이 C
에가 C → F C
있다.
자 함께합시다. position
속성, name
속성 및 setPosition
함수가 있는 클래스를 상상해 봅시다 .
class C
private
x, y : Int
_name : String
public
name : String
position : (Int, Int)
setPosition : (Int, Int) → C
이 클래스를 나타내려면 두 부분이 필요합니다. 먼저 객체의 내부 상태를 나타내야합니다. 이 경우에는 두 개의 Int
a와 a 만 보유 합니다 String
. (이것은 우리 유형 C
입니다.) 그런 다음 클래스를 나타내는 대수를 생각해 내야합니다.
data C = Obj { x, y ∷ Int
, _name ∷ String }
쓸 두 가지 속성이 있습니다. 그들은 아주 사소합니다.
position ∷ C → (Int, Int)
position self = (x self, y self)
name ∷ C → String
name self = _name self
이제 위치를 업데이트 할 수 있어야합니다.
setPosition ∷ C → (Int, Int) → C
setPosition self (newX, newY) = self { x = newX, y = newY }
이것은 명시 적 self
변수를 가진 파이썬 클래스와 같습니다 . 이제 우리는 많은 self →
기능을 가지고 있으므로 대수를위한 단일 기능으로 결합해야합니다. 간단한 튜플 로이 작업을 수행 할 수 있습니다.
coop ∷ C → ((Int, Int), String, (Int, Int) → C)
coop self = (position self, name self, setPosition self)
타입 ((Int, Int), String, (Int, Int) → c)
용 - 어떤이 c
때문에, 펑터를 -is coop
우리가 원하는 형태를 가지고있다 : Functor f ⇒ C → f C
.
이를 감안할 C
와 함께 coop
내가 위에서 준 클래스를 지정하는 coalgebra 형태. 이 같은 기술을 사용하여 객체에 대해 원하는 수의 메서드와 속성을 지정하는 방법을 알 수 있습니다.
이것은 우리가 수업을 다루기 위해 대 수학적 추론을 사용할 수있게합니다. 예를 들어, 클래스 간 변환을 나타 내기 위해 "F- 대수 형 동질성"이라는 개념을 도입 할 수 있습니다. 이것은 무서운 소리의 용어로, 구조를 유지하는 대지 사이의 변형을 의미합니다. 따라서 클래스를 다른 클래스에 매핑하는 것이 훨씬 쉬워집니다.
간단히 말해, F- 대수학은 self
각 객체의 내부 상태를 포함 하는 매개 변수에 의존하는 많은 속성과 메서드를 사용하여 클래스를 나타냅니다 .
지금까지 Haskell 유형의 대수와 대수에 대해 이야기했습니다. 대수학 그냥 유형 τ
함수와 f τ → τ
와 coalgebra 단지 유형 τ
기능과 함께 τ → f τ
.
그러나 실제로 이러한 아이디어를 Haskell 자체 와 관련시키는 것은 없습니다 . 실제로, 이들은 일반적으로 유형 및 하스켈 함수보다는 세트 및 수학 함수로 소개됩니다. 실제로 이러한 개념을 모든 범주로 일반화 할 수 있습니다 !
일부 범주에 대해 F- 대수를 정의 할 수 있습니다 C
. 먼저 functor, F : C → C
즉 endofunctor 가 필요합니다 . (모든 하스켈 Functor
의 사실에서 endofunctors 있습니다 Hask → Hask
.) 그런 다음, 대수는 것을 목적으로한다 A
에서 C
morphism에와 F A → A
. 대수는를 제외하고 동일 A → F A
합니다.
다른 범주를 고려하면 무엇을 얻을 수 있습니까? 글쎄, 우리는 다른 맥락에서 같은 아이디어를 사용할 수 있습니다. 모나드처럼. Haskell에서 모나드는 M ∷ ★ → ★
세 가지 연산 이있는 유형 입니다.
map ∷ (α → β) → (M α → M β)
return ∷ α → M α
join ∷ M (M α) → M α
map
기능은 사실 단지 증거 M
입니다 Functor
. 그래서 우리는 모나드가있는 펑터 그냥 말할 수있는 두 가지 작업이 : return
와 join
.
펑 터는 범주 자체를 형성하며 그 사이의 형태는 소위 "자연 변환"입니다. 자연스러운 변형은 구조를 유지하면서 하나의 functor를 다른 functor로 변형시키는 방법입니다. 여기 아이디어를 설명 돕는 좋은 기사. 에 대한 이야기는 목록에 concat
불과 join
합니다.
Haskell functors를 사용하면 두 functors의 구성이 functor 자체입니다. 의사 코드에서 다음과 같이 작성할 수 있습니다.
instance (Functor f, Functor g) ⇒ Functor (f ∘ g) where
fmap fun x = fmap (fmap fun) x
이를 통해 join
의 매핑으로 생각할 수 있습니다 f ∘ f → f
. 의 유형은 join
입니다 ∀α. f (f α) → f α
. 직관적으로 모든 유형에 유효한 함수 α
가의 변환으로 생각되는 방법을 알 수 있습니다 f
.
return
비슷한 변형입니다. 유형은 ∀α. α → f α
입니다. 이것은 다르게 보인다 – 첫 번째 α
는 펑터 (functor)에 있지 않다! 행복하게도 여기에 ID functor를 추가하여이 문제를 해결할 수 있습니다 ∀α. Identity α → f α
. return
변신도 마찬가지 입니다 Identity → f
.
이제 우리는 어떤 펑를 기반으로 그냥 대수로 모나드에 대해 생각할 수있는 f
작업으로 f ∘ f → f
와 Identity → f
. 익숙하지 않습니까? 그것은 monoid와 매우 유사하며, 이는 τ
연산 τ × τ → τ
과 일부 유형 일뿐 () → τ
입니다.
따라서 모나드는 유형을 갖는 대신 functor를 갖는 것을 제외하고는 monoid와 같습니다. 다른 종류의 대수와 같은 종류입니다. (여기서 "모나드는 endofunctors 범주에서 단일체 일뿐입니다"라는 문구가 내가 아는 곳에서 유래 한 곳입니다.)
이제, 우리는이 두 가지 작업이 : f ∘ f → f
와 Identity → f
. 해당 대수를 얻으려면 화살표를 뒤집습니다. 이것은 우리에게 두 가지 새로운 작업을 제공합니다 : f → f ∘ f
와 f → Identity
. 위와 같이 유형 변수를 추가하여 ∀α. f α → f (f α)
와를 제공하여 Haskell 유형으로 바꿀 수 있습니다 ∀α. f α → α
. 이것은 comonad의 정의와 같습니다.
class Functor f ⇒ Comonad f where
coreturn ∷ f α → α
cojoin ∷ f α → f (f α)
그래서 comonad은 그 다음이다 coalgebra endofunctors의 범주이다.
(,)
과의 신원 펑 ()
. 단일체 범주 내의 단일체 객체는 단일체 대수에 대응하는 화살표가있는 물체로, 제품 유형이 단일체 구조로 Hask의 단일체를 기술합니다. C의 endofunctor 카테고리에서 monoid 객체는 C의 모나드이므로 이해해야합니다. :]
F- 대수와 F- 대수는 유도 유형 (또는 재귀 유형 ) 을 추론하는 데 도움이되는 수학적 구조입니다 .
먼저 F- 대수부터 시작하겠습니다. 최대한 단순하게 노력하겠습니다.
재귀 유형이 무엇인지 알고 있습니다. 예를 들어, 이것은 정수 목록의 유형입니다.
data IntList = Nil | Cons (Int, IntList)
재귀 적이라는 것은 명백합니다. 실제로 그 정의는 자신을 나타냅니다. 정의는 다음과 같은 유형의 두 데이터 생성자로 구성됩니다.
Nil :: () -> IntList
Cons :: (Int, IntList) -> IntList
필자는 단순히 형식이 아닌 Nil
as 형식을 작성했습니다 . 유형은 거주자가 하나뿐 이므로 이론 상으로는 실제로 동등한 유형입니다 .() -> IntList
IntList
()
이러한 함수의 서명을보다 이론적 인 방식으로 작성하면
Nil :: 1 -> IntList
Cons :: Int × IntList -> IntList
여기서 1
유닛 세트 (하나 개의 원소로 설정)하고 A × B
동작이 두 세트의 교차 제품 A
과 B
(즉, 쌍들의 세트는 (a, b)
여기서 a
모든 요소를 통과 A
하고b
모든 요소 통과 B
).
두 세트의 분리 합집합 A
과 B
집합이다 A | B
집합의 합집합 {(a, 1) : a in A}
과 {(b, 2) : b in B}
. 본질적으로 모두 A
와 의 모든 요소 집합 B
이지만이 요소들 각각은 A
또는에 속하는 것으로 '표시'되어 B
있으므로 요소를 선택할 A | B
때이 요소의 출처인지 여부를 즉시 알 수 A
있습니다 B
.
우리는 '결합 (join)' Nil
과 Cons
함수 (function)를 통해 하나의 함수를 하나의 함수로 구성 할 수 있습니다 1 | (Int × IntList)
.
Nil|Cons :: 1 | (Int × IntList) -> IntList
실제로 Nil|Cons
함수가 ()
값에 적용되면 (명백하게 1 | (Int × IntList)
set에 속함 ) 마치 마치 마치 마치 함수 처럼 작동합니다 Nil
. Nil|Cons
유형의 값에 적용되는 경우 (Int, IntList)
(이러한 값도 세트 1 | (Int × IntList)
에 있음)으로 작동합니다 Cons
.
이제 다른 데이터 유형을 고려하십시오.
data IntTree = Leaf Int | Branch (IntTree, IntTree)
다음과 같은 생성자가 있습니다.
Leaf :: Int -> IntTree
Branch :: (IntTree, IntTree) -> IntTree
하나의 함수로 결합 될 수도 있습니다 :
Leaf|Branch :: Int | (IntTree × IntTree) -> IntTree
이 두 joined
기능은 비슷한 유형을 가지고 있음을 알 수 있습니다 .
f :: F T -> T
여기서 F
우리 형 소요 구성보다 복잡한 형태, 범 변형의 종류 x
및 |
작업의 용도 T
가능한 기타 유형. 예를 들어, 대한 IntList
과IntTree
F
외모는 다음과 같습니다 :
F1 T = 1 | (Int × T)
F2 T = Int | (T × T)
우리는 모든 대수적 유형이 이런 식으로 쓰여질 수 있음을 즉시 알 수 있습니다. 사실, 이것이 '대수'라고 불리는 이유는 다른 유형의 많은 '합'(unions)과 'products'(교차 제품)로 구성됩니다.
이제 F- 대수를 정의 할 수 있습니다. F-대수는 단지 한 쌍 (T, f)
, T
몇 가지 유형과 f
유형의 함수이다 f :: F T -> T
. 우리의 예에서 F-대수는 (IntList, Nil|Cons)
와 (IntTree, Leaf|Branch)
. 그러나 해당 유형의 f
기능 에도 불구하고 각 F에 대해 동일 T
하며 f
자체는 임의적 일 수 있습니다. 예를 들어, (String, g :: 1 | (Int x String) -> String)
또는 (Double, h :: Int | (Double, Double) -> Double)
일부 g
및h
해당 F에 대한 F- 대수이기도합니다.
이후에 우리는 F- 대수 동형 과 초기 F- 대수를 도입 할 수 있는데, 이것은 매우 유용한 특성을 가지고 있습니다. 실제로 (IntList, Nil|Cons)
초기 F1- 대수이며 (IntTree, Leaf|Branch)
초기 F2- 대수입니다. 나는이 용어들과 속성들이 필요보다 복잡하고 추상적이기 때문에 정확한 정의를 제시하지 않을 것이다.
그럼에도 불구하고 (IntList, Nil|Cons)
F 대수 라는 사실 은 우리 fold
가이 유형에 대해 비슷한 기능 을 정의 할 수있게 합니다. 아시다시피 fold는 일부 재귀 데이터 유형을 하나의 유한 값으로 변환하는 일종의 연산입니다. 예를 들어, 정수 목록을 단일 값으로 접을 수 있는데, 이는 목록의 모든 요소의 합계입니다.
foldr (+) 0 [1, 2, 3, 4] -> 1 + 2 + 3 + 4 = 10
모든 재귀 데이터 유형에 대해 이러한 작업을 일반화 할 수 있습니다.
다음은 foldr
기능 의 서명입니다 .
foldr :: ((a -> b -> b), b) -> [a] -> b
중괄호를 사용하여 처음 두 인수를 마지막 인수와 구분했습니다. 이것은 실제 foldr
기능은 아니지만 동형입니다 (즉, 쉽게 다른 하나에서 얻을 수 있으며 그 반대도 가능합니다). 부분적으로 적용되는 foldr
서명은 다음과 같습니다.
foldr ((+), 0) :: [Int] -> Int
이것이 정수 목록을 가져와 단일 정수를 반환하는 함수임을 알 수 있습니다. 우리의 IntList
유형으로 이러한 기능을 정의합시다 .
sumFold :: IntList -> Int
sumFold Nil = 0
sumFold (Cons x xs) = x + sumFold xs
우리는이 함수는 두 개의 부분으로 구성되어 있음을 볼 수 : 첫 번째 부분에이 기능의 동작을 정의 Nil
의 일부 IntList
, 그리고 두 번째 부분에 함수의 동작을 정의 Cons
부분이다.
이제 우리가 Haskell이 아니라 타입 서명에서 직접 대수 타입을 사용할 수있게하는 언어로 프로그래밍한다고 가정 해 봅시다 Either a b
. 기능을 고려하십시오.
reductor :: () | (Int × Int) -> Int
reductor () = 0
reductor (x, s) = x + s
F- 대수의 정의 에서처럼 reductor
type의 함수 라는 것을 알 수 있습니다 F1 Int -> Int
! 실제로, 쌍 (Int, reductor)
은 F1 대수입니다.
때문에 IntList
초기 F1 대수 각 타입이며 T
, 각 함수 r :: F1 T -> T
호출하는 기능은 존재, catamorphism 위한 r
, 변환 IntList
에 T
, 이러한 기능은 독특하다. 실제로,이 예에서에 대한 변이는 reductor
입니다 sumFold
. 방법 reductor
과 sumFold
유사함에 유의하십시오 : 구조는 거의 동일합니다! 으로 reductor
정의 s
파라미터 사용량 (유형은 어느에 대응하는 T
의 연산 결과의 사용에 대응한다)를 sumFold xs
에 sumFold
정의.
더 명확하게하고 패턴을 볼 수 있도록 돕기 위해 여기 또 다른 예가 있으며 결과 폴딩 기능부터 다시 시작합니다. append
첫 번째 인수를 두 번째 인수에 추가하는 함수를 고려하십시오 .
(append [4, 5, 6]) [1, 2, 3] = (foldr (:) [4, 5, 6]) [1, 2, 3] -> [1, 2, 3, 4, 5, 6]
이것이 우리의 모습입니다 IntList
:
appendFold :: IntList -> IntList -> IntList
appendFold ys () = ys
appendFold ys (Cons x xs) = x : appendFold ys xs
다시, 리 덕터를 작성해 봅시다 :
appendReductor :: IntList -> () | (Int × IntList) -> IntList
appendReductor ys () = ys
appendReductor ys (x, rs) = x : rs
appendFold
위한 catamorphism이다 appendReductor
되는 변형 IntList
으로 IntList
.
따라서 기본적으로 F- 대수를 사용하면 재귀 데이터 구조, 즉 구조를 일부 값으로 줄이는 작업에 대해 '폴드'를 정의 할 수 있습니다.
F- 대수학은 F- 대수학에 대한 소위 '이중'용어입니다. 이를 통해 unfolds
재귀 데이터 유형, 즉 어떤 값에서 재귀 구조를 구성 할 수있는 방법 을 정의 할 수 있습니다.
다음 유형이 있다고 가정하십시오.
data IntStream = Cons (Int, IntStream)
이것은 무한한 정수 스트림입니다. 유일한 생성자는 다음 유형을 갖습니다.
Cons :: (Int, IntStream) -> IntStream
또는 세트면에서
Cons :: Int × IntStream -> IntStream
Haskell을 사용하면 데이터 생성자에서 패턴 일치를 수행 할 수 있으므로 IntStream
s에서 작동하는 다음 함수를 정의 할 수 있습니다 .
head :: IntStream -> Int
head (Cons (x, xs)) = x
tail :: IntStream -> IntStream
tail (Cons (x, xs)) = xs
자연스럽게 이러한 함수를 유형의 단일 함수로 '결합'할 수 있습니다 IntStream -> Int × IntStream
.
head&tail :: IntStream -> Int × IntStream
head&tail (Cons (x, xs)) = (x, xs)
함수의 결과가 우리 IntStream
유형의 대수적 표현과 어떻게 일치하는지 주목하십시오 . 다른 재귀 데이터 형식에 대해서도 비슷한 작업을 수행 할 수 있습니다. 아마도 당신은 이미 그 패턴을 알아 차렸을 것입니다. 나는 유형의 기능 패밀리를 언급하고 있습니다.
g :: T -> F T
여기서 T
몇 가지 유형입니다. 이제부터는
F1 T = Int × T
이제 F-coalgebra 는 한 쌍 (T, g)
이며, 여기서 T
유형 g
은 유형의 함수입니다 g :: T -> F T
. 예를 들어, (IntStream, head&tail)
F1 대수입니다. 다시, F- 대수에서 g
와 T
같이 임의적 일 수 있으며, 예를 들어, (String, h :: String -> Int x String)
일부 h에 대한 F1- 대수 이기도합니다.
모든 F- 대수학 들 중에서 소위 터미널 F- 대수학이 있는데, 이들은 초기 F- 대수학에 이중이다. 예를 들어, IntStream
터미널 F- 대수입니다. 각 타입에 대해,이 수단은 T
모든 함수 p :: T -> F1 T
호출 된 함수가 존재 anamorphism , 변환 T
에 IntStream
, 이러한 기능을 유일하다.
주어진 함수에서 시작하여 연속적인 정수 스트림을 생성하는 다음 함수를 고려하십시오.
nats :: Int -> IntStream
nats n = Cons (n, nats (n+1))
이제이 기능 검사하자 natsBuilder :: Int -> F1 Int
,이다 natsBuilder :: Int -> Int × Int
:
natsBuilder :: Int -> Int × Int
natsBuilder n = (n, n+1)
다시 말하지만, 우리는 사이에 유사성 볼 수 있습니다 nats
와 natsBuilder
. 이는 리 덕터와 접힘에서 이전에 관찰 한 연결과 매우 유사합니다. nats
의 아나 모피 즘입니다 natsBuilder
.
또 다른 예는 값과 함수를 가져와 함수의 연속적인 응용 프로그램 스트림을 값으로 반환하는 함수입니다.
iterate :: (Int -> Int) -> Int -> IntStream
iterate f n = Cons (n, iterate f (f n))
빌더 기능은 다음과 같습니다.
iterateBuilder :: (Int -> Int) -> Int -> Int × Int
iterateBuilder f n = (n, f n)
그런 다음 iterate
의 아나몰픽입니다 iterateBuilder
.
즉, F- 대수는 폴드를 정의 할 수 있습니다. 즉, 재귀 구조를 단일 값으로 낮추는 연산을 허용하고 F- 대수는 반대를 수행 할 수 있습니다. 단일 값에서 [잠재적으로] 무한 구조를 구성합니다.
실제로 Haskell F- 대수와 F- 대수는 일치합니다. 이것은 각 유형에 '하단'값이 존재하기 때문에 매우 좋은 속성입니다. 따라서 Haskell에서는 모든 재귀 유형에 대해 접기 및 접기를 모두 만들 수 있습니다. 그러나 이것의 배후에있는 이론적 모델은 위에서 제시 한 것보다 더 복잡하므로 의도적으로 피했습니다.
도움이 되었기를 바랍니다.
appendReductor
는 약간 이상하게 보이며 실제로 패턴을 보는 데 도움이되지 않았습니다 ... :) 올바른지 다시 확인할 수 있습니까? .. 일반적으로 리 덕터 유형은 어떻게 생겼습니까? r
거기에 대한 정의 F1
에서 IntList에 의해 결정됩니까, 아니면 임의의 F입니까?
자습서 논문 살펴보기 (공) 대수와 (공) 유도에 관한 자습서 는 컴퓨터 과학의 대수에 대한 통찰력을 제공합니다.
아래는 당신을 설득하기 위해 인용 한 내용입니다.
일반적으로 일부 프로그래밍 언어의 프로그램은 데이터를 조작합니다. 지난 수십 년 동안 컴퓨터 과학이 발전하는 동안, 예를 들어 프로그램이 운영되는 데이터의 특정 표현에 의존하지 않도록하기 위해 이러한 데이터에 대한 추상적 인 설명이 바람직하다는 것이 분명해졌습니다. 또한 이러한 추상화는 정확성 증명을 용이하게합니다.
이러한 요구로 인해 컴퓨터 과학, 대수 사양 또는 추상 데이터 유형 이론이라는 지점에서 대수적 방법이 사용되었습니다. 연구 대상은 대수학에 익숙한 기술 개념을 사용하여 데이터 유형 자체입니다. 컴퓨터 과학자가 사용하는 데이터 유형은 종종 주어진 (생성자) 연산 모음에서 생성되며, 이러한 이유로 대수학의 "초기 성"이 중요한 역할을합니다.
표준 대수 기법은 컴퓨터 과학에 사용되는 데이터 구조의 다양한 필수 측면을 캡처하는 데 유용하다는 것이 입증되었습니다. 그러나 컴퓨팅에서 발생하는 본질적으로 역동적 인 구조 중 일부를 대수적으로 설명하는 것은 어려운 것으로 판명되었습니다. 이러한 구조는 일반적으로 여러 가지 방식으로 변형 될 수있는 상태 개념을 포함합니다. 이러한 상태 기반 동적 시스템에 대한 공식적인 접근 방식은 일반적으로 고전적인 초기 기준으로 오토마타 또는 전환 시스템을 사용합니다.
지난 10 년 동안 이러한 국가 기반 시스템이 대수학이 아니라 소위 공대 수학으로 설명되어야한다는 통찰력이 점차 커졌습니다. 이것들은 대수의 공식적인 이중이며,이 튜토리얼에서 정확하게 만들어 질 것입니다. 대수학에 대한 "처음 성"의 이중 속성, 즉 최종성은 그러한 대수학에 결정적인 것으로 밝혀졌습니다. 그리고 그러한 최종 대수학에 필요한 논리적 추론 원칙은 귀납이 아니라 공동 귀납입니다.
범주 이론에 관한 서문. 범주 이론은 functors 이론의 이름을 바꿔야합니다. 범주는 펑터를 정의하기 위해 정의해야하는 범주입니다. 또한 펑 터는 자연 변환을 정의하기 위해 정의해야하는 기능입니다.
펑터 란? 한 세트에서 다른 세트로 변형하여 구조를 보존합니다. (자세한 내용은 인터넷에 대한 좋은 설명이 많이 있습니다).
F- 대수 란 무엇입니까? functor의 대수입니다. 그것은 단지 functor의 보편적 특성에 대한 연구 일뿐입니다.
어떻게 컴퓨터 과학과 연결될 수 있습니까? 프로그램은 구조화 된 정보 세트로 볼 수 있습니다. 프로그램의 실행은이 구조화 된 정보 세트의 수정에 해당합니다. 실행이 프로그램 구조를 보존해야한다는 것이 좋습니다. 그러면 실행은이 정보 세트에 대한 functor의 응용 프로그램으로 볼 수 있습니다. (프로그램을 정의하는 것).
왜 F-co-algebra인가? 프로그램은 정보에 의해 설명되고 그에 따라 행동하므로 본질적으로 이중적입니다. 그러면 주로 프로그램을 구성하고 변경하게하는 정보를 양방향으로 볼 수 있습니다.
그런 다음이 단계에서
프로그램 수명 동안 데이터와 상태는 공존하며 서로를 완성합니다. 그들은 이중입니다.
나는 분명히 프로그래밍과 관련된 것들로 시작한 다음 수학적인 것들을 추가하여 내가 할 수있는 한 구체적이고 완전한 지구로 유지할 것입니다.
http://www.cs.umd.edu/~micinski/posts/2012-09-04-on-understanding-coinduction.html
유도는 유한 데이터에 관한 것이며, 공동 유도는 무한 데이터에 관한 것입니다.
무한 데이터의 전형적인 예는 지연 목록 (스트림)의 유형입니다. 예를 들어 메모리에 다음과 같은 객체가 있다고 가정 해 봅시다.
let (pi : int list) = (* some function which computes the digits of
π. *)
컴퓨터는 제한된 양의 메모리 만 있기 때문에 π를 모두 보유 할 수는 없습니다! 그러나 그것이 할 수있는 일은 유한 한 프로그램을 유지하는 것인데, 원하는 프로그램을 임의로 길게 확장 할 수 있습니다. 유한 한리스트 만 사용하는 한 필요한만큼 무한한리스트로 계산할 수 있습니다.
그러나 다음 프로그램을 고려하십시오.
let print_third_element (k : int list) = match k with
| _ :: _ :: thd :: tl -> print thd
print_third_element pi
이 프로그램은 pi의 세 번째 숫자를 인쇄해야합니다. 그러나 일부 언어에서는 함수에 대한 인수가 함수에 전달되기 전에 평가됩니다 (지연하지 않고 엄격하지 않은 평가). 이 축소 순서를 사용하면 위의 프로그램은 pi의 숫자를 영원히 계산하여 프린터 기능으로 전달되지 않습니다 (절대로 발생하지 않음). 머신에 무한 메모리가 없기 때문에 결국 메모리 부족 및 충돌이 발생합니다. 최상의 평가 순서가 아닐 수도 있습니다.
http://adam.chlipala.net/cpdt/html/Coinductive.html
Haskell과 같은 게으른 기능 프로그래밍 언어에서는 무한한 데이터 구조가 어디에나 있습니다. 무한리스트 및보다 이국적인 데이터 유형은 프로그램의 부분 간 통신에 편리한 추상화를 제공합니다. 무한 게으른 구조없이 유사한 편의를 달성하려면 많은 경우 제어 흐름의 곡예 반전이 필요합니다.
http://www.alexandrasilva.org/#/talks.html
대수 구조는 일반적으로 다음과 같습니다.
이것은 1. 속성과 2. 메소드를 가진 객체처럼 들립니다. 또는 형식 서명처럼 들립니다.
표준 수학적 예에는 모노 이드 ⊃ 그룹 ⊃ 벡터 공간 ⊃ "대수"가 포함됩니다. 모노 이드는 오토마타 : 동사 시퀀스 (예 :)와 같습니다 f.g.h.h.nothing.f.g.f
. git
항상 결코 역사를 추가하지 않고 로그는이 모노 이드하지만 그룹이 될 것입니다 삭제합니다. 역수를 추가하면 (예 : 음수, 분수, 근, 누적 이력 삭제, 깨진 미러 해제) 그룹이 생깁니다.
그룹에는 함께 더하거나 뺄 수있는 것이 포함됩니다. 예를 들어 Duration
s를 함께 추가 할 수 있습니다. (단, Date
s는 불가능합니다.) 지속 시간은 그룹이 아닌 벡터 공간에 있으며 외부 숫자로도 크기를 조정할 수 있기 때문입니다. (의 형식 서명 scaling :: (Number,Duration) → Duration
)
대수 ⊂ 벡터 공간은 또 다른 일을 할 수 있습니다 m :: (T,T) → T
. "곱셈"이라고 부르거나하지 마십시오. 일단 Integers
"곱셈"(또는 "지수" )이 무엇인지 명확 하지 않기 때문 입니다.
곱셈을해야하는지 그들에게 : 명 (카테고리 이론적) 보편적 인 특성으로 볼 이유 (이다 이렇게 나 처럼 :
)
공배수는 곱셈보다 임의적이지 않은 방식으로 정의하기가 더 쉽습니다. 왜냐하면 T → (T,T)
동일한 요소를 반복해서 사용할 수 있기 때문 입니다. ( "대각선 맵"– 스펙트럼 이론의 대각선 행렬 / 연산자처럼)
코 유닛은 보통 트레이스 (대각선 항목의 합)이지만, 중요한 것은 코 유닛 이하는 일입니다 . trace
행렬에 대한 좋은 대답입니다.
이중 공간 을 보는 이유 는 일반적으로 그 공간에서 생각하기가 더 쉽기 때문입니다. 예를 들어, 법선 벡터에 대해 법선 평면보다 생각하기가 더 쉬운 경우가 있지만 벡터로 평면 (하이 평면 포함)을 제어 할 수 있습니다 (이제 광선 추적기와 같이 익숙한 기하학적 벡터에 대해 말하고 있습니다). .
수학자들은 TQFT 와 같은 재미있는 것을 모델링 하는 반면 프로그래머는
+ :: (Date,Duration) → Date
),Paris
≠ (+48.8567,+2.3508)
! 점이 아닌 모양입니다.),대수학에 대해 이야기 할 때 컴퓨터 과학자들은 대개 직교 곱과 같은 작업을 염두에두고 있습니다. 나는 이것이 "대수학은 하스켈의 대수학"이라고 말할 때 사람들이 의미하는 것이라고 믿는다. 그러나 프로그래머가 Place
, Date/Time
등의 복잡한 데이터 유형을 모델링하고 가능한 Customer
한 실제 모델과 비슷하게 보이도록 (또는 적어도 실제 사용자에 대한 최종 사용자의 견해) 어느 정도 나 유사하게 생각해야합니다. set-world 이상으로 유용 할 수 있습니다.