hotchpotch/wikipedia-multilingual-ir-pairs huggingface dataset:
Initrd 배경
많은 리눅스 배포판은 단일의 일반적인 리눅스 커널 이미지를 제공한다. 이 이미지는 배포판 개발자가 다양한 하드웨어에서 부팅되도록 특별히 생성한 것이다. 이 일반 커널 이미지의 장치 드라이버는 적재 가능 커널 모듈로 포함되는데, 많은 드라이버를 하나의 커널에 정적으로 컴파일하면 커널 이미지가 훨씬 커져서 메모리가 제한된 컴퓨터에서는 부팅하기에 너무 커질 수 있거나, 경우에 따라 존재하지 않거나 충돌하는 하드웨어를 검색하여 부팅 시 충돌 또는 기타 문제가 발생할 수 있기 때문이다. 이 정적 컴파일된 커널 방식은 또한 더 이상 사용되지 않거나 필요 없는 모듈을 커널 메모리에 남기고, 부팅 시 루트 파일 시스템을 마운트하는 데 필요한 모듈을 감지하고 로드하는 문제 또는 루트 파일 시스템이 어디에 있고 무엇인지 추론하는 문제를 발생시킨다.
더 복잡한 문제로, 루트 파일 시스템은 소프트웨어 RAID 볼륨, 논리 볼륨 관리자, 네트워크 파일 시스템 (디스크 없는 워크스테이션에서), 또는 암호화된 파티션에 있을 수 있다. 이 모든 것은 마운트하기 위해 특별한 준비가 필요하다.
Initrd 배경
또 다른 복잡한 문제는 하이버네이션에 대한 커널 지원인데, 이는 컴퓨터의 모든 메모리 내용을 스왑 파티션이나 일반 파일로 덤프한 다음 전원을 끄는 방식으로 컴퓨터를 디스크에 일시 중단시킨다. 다음 부팅 시에는 이 이미지를 메모리로 다시 로드하기 전에 액세스 가능하게 만들어야 한다.
커널에 너무 많은 특수 사례를 하드코딩하는 것을 피하기 위해, 임시 루트 파일 시스템—현재는 초기 사용자 공간이라고 불리는—을 사용하는 초기 부팅 단계가 사용된다. 이 루트 파일 시스템은 실제 파일 시스템을 마운트하는 데 필요한 하드웨어 감지, 모듈 로딩 및 장치 검색을 수행하는 사용자 공간 도우미를 포함할 수 있다.
Initrd 구현
이 초기 루트 파일 시스템의 디스크 이미지는 (커널 이미지와 함께) 부트로더 또는 컴퓨터의 부팅 펌웨어가 액세스할 수 있는 어딘가에 저장되어야 한다. 이것은 루트 파일 시스템 자체, 광 디스크의 부팅 이미지, 로컬 디스크의 작은 파티션(부팅 파티션, 일반적으로 ext2 또는 파일 할당 테이블 파일 시스템 사용), 또는 TFTP 서버( 이더넷에서 부팅할 수 있는 시스템에서)일 수 있다.
부트로더는 커널과 초기 루트 파일 시스템 이미지를 메모리로 로드한 다음 커널을 시작하고 이미지의 메모리 주소를 전달한다. 부팅 시퀀스 끝에서 커널은 데이터의 처음 몇 블록에서 이미지의 형식을 결정하려고 시도하며, 이는 initrd 또는 initramfs 방식으로 이어질 수 있다.
Initrd 구현
initrd 방식에서 이미지는 파일 시스템 이미지(선택적으로 압축됨)일 수 있으며, 이는 특별한 블록 장치(/dev/ram)에서 사용할 수 있게 되어 초기 루트 파일 시스템으로 마운트된다. 해당 파일 시스템의 드라이버는 커널에 정적으로 컴파일되어야 한다. 많은 배포판은 원래 압축된 ext2 파일 시스템 이미지를 사용했지만, 다른 배포판(데비안 3.1 포함)은 cramfs 이미지가 압축 해제를 위한 추가 공간을 필요로 하지 않고 제자리에서 마운트될 수 있기 때문에 메모리가 제한된 시스템에서 부팅하기 위해 cramfs를 사용했다. 초기 루트 파일 시스템이 실행되면 커널은 /linuxrc를 첫 번째 프로세스로 실행한다. 종료되면 커널은 실제 루트 파일 시스템이 마운트되었다고 가정하고 /sbin/init를 실행하여 일반 사용자 공간 부팅 프로세스를 시작한다.
Initrd 구현
initramfs 방식(리눅스 커널 2.6.13부터 사용 가능)에서는 이미지가 cpio 아카이브(선택적으로 압축됨) 또는 그러한 아카이브의 연결일 수 있다. 아카이브는 커널에 의해 특별한 tmpfs 인스턴스로 압축 해제되어 초기 루트 파일 시스템이 된다. 이 방식은 중간 파일 시스템이나 블록 드라이버가 커널에 컴파일될 필요가 없다는 장점이 있다. 일부 시스템은 dracut 패키지를 사용하여 initramfs 이미지를 생성한다. initramfs 방식에서는 커널이 /init를 종료되지 않을 것으로 예상되는 첫 번째 프로세스로 실행한다. 일부 응용 프로그램의 경우 initramfs는 'casper' 유틸리티를 사용하여 unionfs를 활용하여 읽기 전용 루트 파일 시스템 이미지 위에 지속성 계층을 오버레이하는 쓰기 가능한 환경을 생성할 수 있다. 예를 들어, 오버레이 데이터는 USB 플래시 드라이브에 저장되고, 압축된 SquashFS 읽기 전용 이미지는 라이브 CD에 저장되어 루트 파일 시스템 역할을 한다.
Initrd 구현
커널에 정적으로 컴파일된 알고리즘에 따라 커널은 gzip, bzip2, LZMA, XZ, LZO, LZ4, 및 zstd로 압축된 initrd/initramfs 이미지를 압축 해제할 수 있다.
데비안과 같은 일부 리눅스 배포판은 ATA, SCSI 및 파일 시스템 커널 모듈과 같이 특정 컴퓨터를 부팅하는 데 필요한 것만 포함하는 맞춤형 initrd 이미지를 생성한다. 이러한 이미지에는 일반적으로 루트 파일 시스템의 위치와 유형이 포함되어 있다.
다른 리눅스 배포판(페도라 및 우분투 등)은 보다 일반적인 initrd 이미지를 생성한다. 이들은 루트 파일 시스템의 장치 이름(UUID 포함)으로만 시작하며 부팅 시 다른 모든 것을 찾아야 한다. 이 경우 소프트웨어는 루트 파일 시스템을 마운트하기 위해 복잡한 일련의 작업을 수행해야 한다.
일부 배포판은 이벤트 기반 핫플러그 에이전트인 udev를 사용하는데, 이는 특정 규칙과 일치하는 하드웨어 장치, 디스크 파티션 및 스토리지 볼륨이 온라인 상태가 될 때 도우미 프로그램을 호출한다. 이를 통해 검색이 병렬로 실행되고 LVM, RAID 또는 암호화의 임의 중첩으로 점진적으로 이어져 루트 파일 시스템에 도달할 수 있다.
Initrd 구현
루트 파일 시스템이 마침내 보이게 되면, 마운트된 루트 파일 시스템에서 실행할 수 없는 유지 보수 작업이 수행되고, 루트 파일 시스템은 읽기 전용으로 마운트되며, 계속 실행되어야 하는 모든 프로세스(예: 스플래시 스크린 도우미 및 해당 명령 FIFO)는 새로 마운트된 루트 파일 시스템으로 이동된다.
최종 루트 파일 시스템은 단순히 /에 마운트될 수 없다. 그렇게 하면 초기 루트 파일 시스템의 스크립트와 도구가 최종 정리 작업을 위해 접근할 수 없게 되기 때문이다.
initrd에서는 새 루트가 임시 마운트 지점에 마운트된 다음 pivot_root(8) (이 목적을 위해 특별히 도입됨)를 사용하여 제자리로 회전된다. 이로 인해 초기 루트 파일 시스템은 마운트 지점(예: /initrd)에 남아 있게 되며, 나중에 일반 부팅 스크립트가 이를 언마운트하여 initrd가 차지하는 메모리를 해제할 수 있다.
Initrd 구현
대부분의 초기 루트 파일 시스템은 /linuxrc 또는 /init를 셸 스크립트로 구현하므로 최소한의 셸(일반적으로 /bin/ash)과 일부 필수 사용자 공간 유틸리티(일반적으로 비지박스 툴킷)를 포함한다. 공간을 더욱 절약하기 위해 셸, 유틸리티 및 해당 지원 라이브러리는 일반적으로 공간 최적화가 활성화된 상태(예: gcc의 "-Os" 플래그 사용)로 컴파일되고, 이 목적을 위해 특별히 작성된 최소 버전의 C 라이브러리인 klibc에 링크된다.
리눅스 배포판 설치 프로그램은 일반적으로 initramfs에서 전적으로 실행되는데, 영구 저장소가 설정되기 전에 설치 프로그램 인터페이스와 지원 도구를 호스팅할 수 있어야 하기 때문이다.
타이니 코어 리눅스 및 퍼피 리눅스는 initrd에서 전적으로 실행될 수 있다.
Windows Vista 이후로 Windows는 WIM 디스크 이미지 파일에서 부팅할 수 있으며, 이 파일 형식은 공개되어 있다. 이는 하드 링크, 중복 제거된 청크를 지원하고 청크별 압축을 사용한다는 점을 제외하면 ZIP 형식과 유사하다. 이 경우 전체 WIM이 초기에는 RAM으로 로드된 다음 커널 초기화가 이어진다. 다음으로 로드된 WIM은 할당된 드라이브 문자와 함께 SystemRoot로 사용할 수 있다. Windows 설치 프로그램은 이를 사용하여 BOOT.WIM에서 부팅한 다음 INSTALL.WIM을 설치할 Windows 파일 모음으로 사용한다.
또한 윈도우 사전 설치 환경 (Windows PE)도 동일한 방식을 사용하며, 일부 안티바이러스 및 백업/재해 복구 소프트웨어의 별도 부팅 버전의 기반이 된다.
물리적 드라이브에 있는 WIM 또는 VHD 파일에서 항상 부팅하도록 Windows를 설치하는 것도 가능하다. 그러나 Windows 부트로더는 부팅 시 커널 모듈에 대한 .sys 파일을 직접 로드할 수 있으므로 Linux에서 initrd가 필요한 작업은 거의 사용되지 않는다.
모나드 (함수형 프로그래밍)
함수형 프로그래밍에서 모나드(monad)는 계산을 일련의 단계로 구성하는 방법으로, 각 단계는 값뿐만 아니라 잠재적 실패, 비결정성 또는 부작용과 같은 계산에 대한 추가 정보를 생성한다. 더 형식적으로, 모나드는 두 가지 연산을 갖춘 형식 생성자 M이다. return : <A>(a : A) -> M(A)는 값을 모나드 문맥으로 올리고, bind : <A,B>(m_a : M(A), f : A -> M(B)) -> M(B)는 모나드 계산을 연결한다. 간단히 말해, 모나드는 모나드를 구현하는 다양한 형식 생성자 변형(예: Option, List 등)에 대해 함수가 추상화할 수 있도록 하는 인터페이스로 생각할 수 있다.
모나드의 개념과 용어는 원래 범주론에서 유래했으며, 여기서 모나드는 추가 구조를 갖춘 자기 함자로 정의된다. 1980년대 후반과 1990년대 초반에 시작된 연구는 모나드가 겉보기에는 서로 다른 컴퓨터 과학 문제를 통일된 함수형 모델로 통합할 수 있음을 입증했다. 범주론은 또한 모나드 법칙으로 알려진 몇 가지 형식적 요구 사항을 제공하며, 이는 모든 모나드에 의해 충족되어야 하며 모나드 코드를 검증하는 데 사용될 수 있다.
모나드 (함수형 프로그래밍)
모나드는 특정 종류의 계산에 대한 의미론을 명시적으로 만들기 때문에 편리한 언어 기능을 구현하는 데 사용될 수도 있다. 하스켈과 같은 일부 언어는 일반적인 모나드 구조와 공통 인스턴스를 위한 사전 빌드된 정의를 핵심 라이브러리에 제공하기도 한다.
모나드 (함수형 프로그래밍) 개요
"모나드 m의 경우, 타입 m a의 값은 모나드의 맥락 내에서 타입 a의 값에 접근할 수 있음을 나타낸다." — C. A. McCann
더 정확히 말하면, 모나드는 시나리오에 특정한 이유로 값에 대한 무제한적인 접근이 부적절한 곳에서 사용될 수 있다. Maybe 모나드의 경우, 값이 존재하지 않을 수 있기 때문이다. IO 모나드의 경우, 값이 아직 알려지지 않았을 수 있기 때문인데, 예를 들어 모나드가 프롬프트가 표시된 후에만 제공될 사용자 입력을 나타낼 때 그렇다. 모든 경우에 접근이 의미 있는 시나리오는 모나드에 대해 정의된 bind 연산에 의해 포착된다. Maybe 모나드의 경우 값이 존재할 때만 바인딩되고, IO 모나드의 경우 시퀀스의 이전 연산이 수행된 후에만 바인딩된다.
모나드는 형식 생성자 M과 두 가지 연산을 정의하여 생성할 수 있다.
(bind 연산자 대신 join 함수를 사용하는 대안적이지만 동등한 구성은 나중에 § Derivation from functors 섹션에서 찾을 수 있다.)
모나드 (함수형 프로그래밍) 개요
이러한 요소들을 사용하여 프로그래머는 여러 bind 연산자를 식으로 연결하여 함수 호출 시퀀스("파이프라인")를 구성한다. 각 함수 호출은 입력 일반형 값을 변환하고, bind 연산자는 반환된 모나드 값을 처리하여 시퀀스의 다음 단계로 공급한다.
일반적으로 바인드 연산자 >>=는 매개변수로 받은 함수에는 없는 추가 계산 단계를 수행하는 모나드 고유의 코드를 포함할 수 있다. 구성된 함수 호출 쌍 사이에서 바인드 연산자는 함수 f 내에서 접근할 수 없는 추가 정보를 모나드 값 m a에 주입하고 파이프라인을 따라 전달할 수 있다. 또한 함수를 특정 조건에서만 호출하거나 함수 호출을 특정 순서로 실행하는 등 실행 흐름을 더욱 세밀하게 제어할 수도 있다.
모나드 (함수형 프로그래밍) 개요
모나드의 한 가지 예는 Maybe 타입이다. 정의되지 않은 null 결과는 많은 절차적 언어가 특별한 도구를 제공하지 않는 특정 문제점으로, 널 오브젝트 패턴을 사용하거나 각 연산에서 유효하지 않은 값에 대한 검사를 통해 정의되지 않은 값을 처리해야 한다. 이로 인해 버그가 발생하고 오류를 우아하게 처리하는 견고한 소프트웨어를 구축하기가 더 어려워진다. Maybe 타입은 프로그래머가 결과의 두 상태를 명시적으로 정의하여 이러한 잠재적으로 정의되지 않은 결과를 처리하도록 강제한다. 즉, Just ⌑result⌑ 또는 Nothing이다. 예를 들어, 프로그래머는 중간 결과를 반환하거나, 파서가 감지한 조건을 알리고 프로그래머가 처리해야 하는 파서를 구축할 수 있다. 약간의 함수적 양념을 추가하면 이 Maybe 타입은 완전한 기능을 갖춘 모나드로 변환된다.:12.3 pages 148–151
모나드 (함수형 프로그래밍) 개요
대부분의 언어에서 Maybe 모나드는 옵션 타입으로도 알려져 있는데, 이는 값이 포함되어 있는지 여부를 표시하는 타입일 뿐이다. 일반적으로 열거형의 한 형태로 표현된다. 러스트 프로그래밍 언어에서는 Option<T>라고 불리며, 이 타입의 변형은 제네릭 타입 T의 값이거나 빈 변형인 None일 수 있다.
Option<T>는 "래핑" 타입으로도 이해될 수 있으며, 이것이 모나드와의 연관성이 나타나는 지점이다. Maybe 타입을 사용하는 언어에서는 모나드 함수를 서로 구성하고 Maybe가 값을 포함하는지 여부를 테스트하는 데 도움이 되는 함수가 있다.
다음 하드코딩된 예제에서는 Maybe 타입이 실패할 수 있는 함수의 결과로 사용된다. 이 경우 0으로 나누기가 있으면 아무것도 반환하지 않는다.
Maybe가 값을 포함하는지 여부를 테스트하는 한 가지 방법은 if 문을 사용하는 것이다.
다른 언어는 패턴 매칭을 가질 수 있다.
모나드 (함수형 프로그래밍) 개요
모나드는 Maybe를 반환하는 함수들을 결합하여 함께 사용할 수 있다. 구체적인 예시에서는 여러 Maybe 매개변수를 받아서, 다음 예시와 같이 어떤 매개변수라도 Nothing일 경우 그 값이 Nothing인 단일 Maybe를 반환하는 함수를 가질 수 있다.
Some 표현식을 반복하는 대신 바인드 연산자라고 불리는 것을 사용할 수 있다. (또한 "map", "flatmap", 또는 "shove"라고도 알려져 있다.:2205s). 이 연산은 모나드와 모나드를 반환하는 함수를 받아서, 전달된 모나드의 내부 값에 함수를 실행하고, 함수에서 모나드를 반환한다.
하스켈에서는 바인드 연산자 또는 (>>=)가 함수의 합성과 유사한 보다 우아한 형태로 이러한 모나드 합성을 가능하게 한다.:150–151
>>=를 사용할 수 있게 되면서, chainable_division은 익명 함수(즉, 람다)의 도움으로 훨씬 더 간결하게 표현될 수 있다. 아래 표현식에서 두 개의 중첩된 람다가 바인드 연산자를 사용하여 전달된 Maybe 모나드 안의 래핑된 값에서 각각 어떻게 작동하는지 주목하라.:93
모나드 (함수형 프로그래밍) 개요
지금까지 보여준 것은 기본적으로 모나드이지만, 더 간결하게 말하면, 다음은 다음 섹션에서 정의된 모나드에 필요한 엄격한 속성 목록이다.
모나드 타입
타입 (Maybe):148–151
단위 연산
타입 변환기 (Just(x)):93
바인드 연산
모나드 함수를 위한 조합기 (>>= 또는 .flatMap()):150–151
이것들이 모나드를 구성하는 데 필요한 세 가지 요소이다. 다른 모나드는 다른 논리적 프로세스를 구현할 수 있으며, 일부는 추가 속성을 가질 수도 있지만, 이 세 가지 유사한 구성 요소를 모두 가질 것이다.
위 예제에서 사용된 함수형 프로그래밍에서의 모나드에 대한 보다 일반적인 정의는 사실 범주론의 표준 정의가 아닌 클라이슬리 삼중체 ⟨T, η, μ⟩에 기반한다. 그러나 두 구성은 수학적으로 동등한 것으로 판명되었으므로 어떤 정의든 유효한 모나드를 산출할 것이다. 모든 잘 정의된 기본 타입 T와 U가 주어졌을 때, 모나드는 세 부분으로 구성된다.
모나드 (함수형 프로그래밍) 개요
일반적으로 bind (변수를 바인딩하는 것처럼)라고 불리고 중위 연산자 >>= 또는 flatMap이라는 메서드로 표현되는 조합기는 모나드 변수를 풀고, 이를 모나드 함수/표현식에 삽입하여 새로운 모나드 값을 생성한다.(>>=) : (M T, T → M U) → M U 따라서 ma : M T이고 f : T → M U이면,  (ma >>= f) : M U
그러나 모나드로 완전히 자격을 갖추려면 이 세 부분이 몇 가지 법칙을 준수해야 한다.
대수적으로, 이는 모든 모나드가 클라이슬리 범주라고 불리는 범주와 함자(값에서 계산으로) 범주의 모노이드를 생성하며, 모나드 합성이 모노이드의 이항 연산자:2450s이고 unit이 모노이드의 항등원임을 의미한다.
모나드 (함수형 프로그래밍) 개요
모나드 패턴의 가치는 단순히 코드를 압축하고 수학적 추론에 대한 링크를 제공하는 것을 넘어선다.
개발자가 어떤 언어나 기본 프로그래밍 패러다임을 사용하든, 모나드 패턴을 따르면 순수 함수형 프로그래밍의 많은 이점을 얻을 수 있다.
특정 종류의 계산을 재화함으로써, 모나드는 그 계산 패턴의 지루한 세부 사항을 캡슐화할 뿐만 아니라, 선언적 방식으로 이를 수행하여 코드의 명확성을 향상시킨다.
모나드 값은 계산된 값뿐만 아니라 계산된 효과도 명시적으로 나타내기 때문에, 순수 표현식처럼 모나드 표현식은 참조 투명한 위치에서 그 값으로 대체될 수 있으며, 재작성에 기반한 많은 기술과 최적화를 가능하게 한다.
일반적으로 프로그래머는 bind를 사용하여 모나드 함수를 시퀀스로 연결하며, 이로 인해 일부는 모나드를 "프로그래밍 가능한 세미콜론"이라고 묘사하기도 하는데, 이는 많은 명령형 프로그래밍 언어가 세미콜론을 사용하여 명령문을 구분하는 방식을 참조한다.
그러나 모나드는 실제로 계산 순서를 정하지 않는다. 심지어 모나드를 핵심 기능으로 사용하는 언어에서도 더 간단한 함수 합성을 통해 프로그램 내의 단계를 정렬할 수 있다.
모나드의 일반적인 유용성은 추상화를 통해 프로그램의 구조를 단순화하고 관심사 분리를 개선하는 데 있다.
모나드 구조는 또한 컴파일 타임에 데코레이터 패턴에 대한 고유한 수학적 변형으로 볼 수 있다.
일부 모나드는 함수에 접근할 수 없는 추가 데이터를 전달할 수 있으며, 일부는 특정 조건에서만 함수를 호출하는 등 실행에 대한 더 세밀한 제어를 행사하기도 한다.
애플리케이션 프로그래머가 도메인 논리를 구현하면서 상용구 코드를 미리 개발된 모듈로 오프로드할 수 있게 해주기 때문에, 모나드는 관점 지향 프로그래밍을 위한 도구로도 간주될 수 있다.
모나드의 또 다른 주목할 만한 용도는 입출력 또는 가변 상태와 같은 부작용을 순수 함수형 코드에서 격리하는 것이다.
순수 함수형 언어도 모나드 없이도 이러한 "불순한" 계산을 구현할 수 있는데, 특히 복잡한 함수 합성 및 연속 전달 스타일 (CPS)의 조합을 통해서 가능하다.
그러나 모나드를 사용하면 이러한 많은 스캐폴딩을 추상화할 수 있으며, 본질적으로 CPS 코드의 각 반복 패턴을 별개의 모나드로 묶는 방식이다.
언어가 기본적으로 모나드를 지원하지 않더라도 패턴을 구현하는 것은 여전히 가능하며, 종종 큰 어려움 없이 구현할 수 있다.
범주론에서 프로그래밍 용어로 번역될 때, 모나드 구조는 제네릭 개념이며 제한된 다형성에 상응하는 기능을 지원하는 모든 언어에서 직접 정의할 수 있다.
개념이 기본 타입에서 작동하면서도 운영 세부 사항에 대해 불가지론적일 수 있는 능력은 강력하지만, 모나드의 고유한 기능과 엄격한 동작은 다른 개념과 구별된다.
모나드 (함수형 프로그래밍) 응용
특정 모나드에 대한 논의는 주어진 모나드가 특정 계산 형식을 나타내므로 좁은 구현 문제를 해결하는 데 중점을 둔다.
그러나 일부 상황에서는 애플리케이션이 핵심 논리 내에서 적절한 모나드를 사용하여 고수준 목표를 달성할 수도 있다.
다음은 디자인의 핵심에 모나드를 사용하는 몇 가지 응용 프로그램이다.
모나드 (함수형 프로그래밍) 역사
프로그래밍에서 "모나드"라는 용어는 순수 함수형 경향이 있는 APL 및 J 프로그래밍 언어에서 유래했다. 그러나 이들 언어에서 "모나드"는 단일 매개변수를 취하는 함수(두 매개변수를 취하는 함수는 "다이어드" 등으로 불림)의 약어일 뿐이다.
수학자 로제 고드망은 1950년대 후반에 모나드 개념(이를 "표준 구성"이라고 명명)을 처음으로 정립했지만, 널리 퍼지게 된 "모나드"라는 용어는 범주론자인 손더스 매클레인에 의해 대중화되었다. 그러나 위에서 bind를 사용하여 정의된 형태는 원래 1965년 수학자 하인리히 클라이슬리가 모든 모나드가 두 (공변) 함자 사이의 수반으로 특징지어질 수 있음을 증명하기 위해 기술했다.
모나드 (함수형 프로그래밍) 역사
1980년대부터 모나드 패턴에 대한 모호한 개념이 컴퓨터 과학 커뮤니티에서 나타나기 시작했다.
프로그래밍 언어 연구원인 필립 와들러에 따르면, 컴퓨터 과학자 존 C. 레이놀즈는 1970년대와 1980년대 초반에 연속 전달 스타일의 가치, 형식 의미론의 풍부한 원천으로서의 범주론, 그리고 값과 계산 사이의 타입 구별에 대해 논하면서 모나드의 여러 측면을 예상했다.
1990년까지 활발히 설계되었던 연구 언어 오팔도 I/O를 모나드 타입에 효과적으로 기반했지만, 당시에는 그 연결이 인식되지 않았다.
컴퓨터 과학자 유제니오 모지는 1989년 컨퍼런스 논문에서 범주론의 모나드를 함수형 프로그래밍에 명시적으로 연결한 최초의 인물이었고, 이어서 1991년에는 더욱 정교한 저널 논문을 발표했다. 이전 연구에서 여러 컴퓨터 과학자들은 람다 대수에 대한 의미론을 제공하기 위해 범주론을 사용하는 것을 발전시켰다. 모지의 핵심 통찰력은 실제 프로그램이 단순히 값에서 다른 값으로의 함수가 아니라, 그 값에 대한 계산을 형성하는 변환이라는 것이었다. 이를 범주론적 용어로 형식화하면 모나드가 이러한 계산을 나타내는 구조라는 결론에 이르게 된다.
모나드 (함수형 프로그래밍) 역사
필립 와들러와 사이먼 페이튼 존스를 포함한 다른 여러 사람들이 이 아이디어를 대중화하고 발전시켰으며, 이들 모두는 하스켈의 사양에 참여했다. 특히 하스켈은 v1.2까지 I/O를 느긋한 계산법과 조화시키기 위해 문제가 있는 "느긋한 스트림" 모델을 사용하다가, 더 유연한 모나드 인터페이스로 전환했다. 하스켈 커뮤니티는 함수형 프로그래밍의 많은 문제에 모나드를 적용했으며, 2010년대에 하스켈을 연구하는 학자들은 결국 모나드가 어플리커티브 함자라는 것을 인식했다. 그리고 모나드와 화살 모두 모노이드이다.
처음에는 모나드를 사용한 프로그래밍이 주로 하스켈과 그 파생 언어에 국한되었지만, 함수형 프로그래밍이 다른 패러다임에 영향을 미치면서 많은 언어가 모나드 패턴(이름은 아닐지라도 정신적으로)을 통합했다. 현재 스킴, 펄, 파이썬, 래킷, 클로저, 스칼라, F#에서 형식화가 존재하며, 새로운 ML 표준에 대해서도 고려되었다.
모나드 (함수형 프로그래밍) 분석
모나드 패턴의 한 가지 이점은 계산 합성에 대한 수학적 정밀성을 제공한다는 것이다.
모나드 법칙을 사용하여 인스턴스의 유효성을 확인할 수 있을 뿐만 아니라, (함자와 같은) 관련 구조의 기능도 서브타이핑을 통해 사용할 수 있다.
Maybe 예제로 돌아가서, 그 구성 요소들은 모나드를 구성한다고 선언되었지만, 모나드 법칙을 만족한다는 증명은 주어지지 않았다.
이것은 일반 법칙의 한쪽에 Maybe의 세부 사항을 대입한 다음, 대수적으로 등식의 사슬을 구성하여 다른 쪽에 도달함으로써 해결될 수 있다.
컴퓨터 과학에서는 더 드물지만, 함자와 두 가지 추가적인 자연 변환을 가진 모나드를 직접적으로 범주론에서 정의할 수 있다.
따라서 시작하려면 구조가 map이라는 고차 함수 (또는 "함수형")를 가져야 함자의 자격을 갖춘다.
그러나 이것이 항상 큰 문제는 아니다. 특히 모나드가 기존 함자에서 파생될 경우 모나드는 map을 자동으로 상속받기 때문이다. (역사적인 이유로, 이 map은 하스켈에서 fmap이라고 불린다.)
모나드 (함수형 프로그래밍) 분석
모나드의 첫 번째 변환은 사실 클라이슬리 삼중체와 동일한 unit이지만, 구조의 계층을 면밀히 따르면 unit은 모나드와 기본 함자 사이의 중간 구조인 어플리커티브 함자를 특징짓는 것으로 밝혀진다. 어플리커티브 컨텍스트에서 unit은 때때로 pure라고 불리지만 여전히 동일한 함수이다. 이 구성에서 다른 점은 unit이 만족해야 하는 법칙이다. bind가 정의되지 않았으므로 제약 조건은 map의 관점에서 주어진다.
어플리커티브 함자에서 모나드로의 마지막 도약은 두 번째 변환인 join 함수(범주론에서는 일반적으로 μ라고 불리는 자연 변환)와 함께 이루어지며, 이는 중첩된 모나드 적용을 "평탄화"한다.
특성 함수로서 join 또한 모나드 법칙에 대한 세 가지 변형을 만족해야 한다.
개발자가 직접 모나드를 정의하든 클라이슬리 삼중체를 정의하든, 기본 구조는 동일하며 형식은 서로 쉽게 파생될 수 있다.
모나드 (함수형 프로그래밍) 분석
리스트 모나드는 더 간단한 함자에서 모나드를 파생시키는 것이 얼마나 유용한지를 자연스럽게 보여준다.
많은 언어에서 리스트 구조는 일부 기본 기능과 함께 미리 정의되어 있으므로, List 형식 생성자와 추가 연산자(중위 표기법의 경우 ++로 표시됨)는 여기에서 이미 주어진 것으로 가정한다.
일반 값을 리스트에 임베딩하는 것은 대부분의 언어에서 사소하다.
여기서부터 리스트 캄프리헨션을 사용하여 함수를 반복적으로 적용하는 것이 bind 및 리스트를 완전한 모나드로 변환하는 쉬운 선택처럼 보일 수 있다.
이 접근 방식의 어려움은 bind가 모나드 함수를 기대하며, 이 경우 자체가 리스트를 출력한다는 것이다.
더 많은 함수가 적용될수록 중첩된 리스트 레이어가 축적되어 기본적인 컴프리헨션 이상이 필요하다.
그러나 전체 리스트에 간단한 함수를 적용하는 절차, 즉 map은 간단하다.
이제 이 두 절차는 이미 List를 어플리커티브 함자로 승격시킨다.
모나드로 완전히 자격을 갖추려면 반복되는 구조를 평탄화하는 올바른 join 개념만 필요하지만, 리스트의 경우 이는 외부 리스트를 풀어서 값을 포함하는 내부 리스트를 추가하는 것을 의미한다.
모나드 (함수형 프로그래밍) 분석
결과적으로 생성된 모나드는 리스트일 뿐만 아니라 함수가 적용될 때마다 자동으로 크기가 조정되고 자체적으로 응축되는 리스트이다.
bind는 이제 공식만으로도 파생될 수 있으며, List 값을 모나드 함수의 파이프라인을 통해 공급하는 데 사용될 수 있다.
이 모나드 리스트의 한 가지 응용 프로그램은 비결정적 계산을 나타내는 것이다.
List는 알고리즘의 모든 실행 경로에 대한 결과를 보유한 다음, 각 단계에서 자체적으로 응축하여 어떤 경로가 어떤 결과로 이어졌는지 "잊을" 수 있다 (때로는 결정적이고 완전한 알고리즘과 중요한 구별).
또 다른 이점은 검사를 모나드에 내장할 수 있다는 것이다. 특정 경로는 파이프라인의 함수를 다시 작성할 필요 없이 실패 지점에서 투명하게 가지치기될 수 있다.
List가 빛을 발하는 두 번째 상황은 다가 함수를 구성하는 것이다.
예를 들어, 어떤 수의 n번째 복소근은 n개의 서로 다른 복소수를 생성해야 하지만, 그 결과에 대해 또 다른 m번째 근이 취해지면 최종 m•n개의 값은 m•n번째 근의 출력과 동일해야 한다.
List는 이 문제를 완전히 자동화하여 각 단계의 결과를 평탄하고 수학적으로 올바른 리스트로 압축한다.
모나드 (함수형 프로그래밍) 기법
모나드는 단순히 프로그램 논리를 구성하는 것을 넘어 흥미로운 기법들을 제공한다. 모나드는 유용한 문법적 기능의 토대를 마련할 수 있으며, 그 고수준 및 수학적 특성은 상당한 추상화를 가능하게 한다.
bind를 공개적으로 사용하는 것이 종종 의미가 있지만, 많은 프로그래머는 명령문과 유사한 구문(하스켈에서는 do-notation, OCaml에서는 perform-notation, F#에서는 계산 표현식, 스칼라에서는 for comprehension)을 선호한다. 이는 모나드 파이프라인을 코드 블록으로 위장하는 단순한 신택틱 슈거일 뿐이며, 컴파일러는 이러한 표현식을 기본 함수형 코드로 조용히 변환한다.
Maybe에서 add 함수를 하스켈로 번역하면 이 기능을 확인할 수 있다. 하스켈에서 add의 비모나드 버전은 다음과 같다.
모나드 하스켈에서 return은 unit의 표준 이름이며, 람다 표현식은 명시적으로 처리해야 하지만, 이러한 기술적인 문제에도 불구하고 Maybe 모나드는 더 깔끔한 정의를 제공한다.
하지만 do-표기법을 사용하면 이 과정을 더욱 직관적인 순서로 간결화할 수 있다.
모나드 (함수형 프로그래밍) 기법
두 번째 예는 Maybe가 완전히 다른 언어인 F#에서 어떻게 사용될 수 있는지를 보여준다.
계산 표현식을 사용하면, 정의되지 않은 피연산자나 0으로 나누기 연산에 대해 None을 반환하는 "안전한 나누기" 함수를 다음과 같이 작성할 수 있다.
빌드 시 컴파일러는 이 함수를 내부적으로 더 밀집된 bind 호출 체인으로 "디슈거링"할 것이다.
마지막 예시로, 일반 모나드 법칙 자체도 do-표기법으로 표현할 수 있다.
모든 모나드는 모나드 법칙을 만족하는 특정 구현이 필요하지만, 다른 구조와의 관계나 언어 내의 표준 관용구와 같은 다른 측면은 모든 모나드에서 공유된다.
결과적으로 언어 또는 라이브러리는 함수 원형, 서브타이핑 관계 및 기타 일반적인 사실을 포함하는 일반적인 Monad 인터페이스를 제공할 수 있다.
개발을 시작하고 새로운 모나드가 슈퍼타입(함자와 같은)의 기능을 상속받도록 보장하는 것 외에도, 모나드의 설계를 인터페이스와 비교하여 확인하는 것은 품질 관리의 또 다른 계층을 추가한다.
모나드 (함수형 프로그래밍) 기법
모나드 코드는 종종 연산자를 현명하게 사용함으로써 더욱 단순화될 수 있다.
map 함수형은 특히 유용할 수 있는데, 이는 임시 모나드 함수뿐만 아니라 다양한 경우에 작동하기 때문이다. 모나드 함수가 미리 정의된 연산자와 유사하게 작동해야 하는 한, map을 사용하여 간단한 연산자를 즉시 모나드 연산자로 "올릴" 수 있다.
이 기법을 사용하면 Maybe 예제의 add 정의를 다음으로 요약할 수 있다.
이 과정은 add를 Maybe뿐만 아니라 전체 Monad 인터페이스에 대해 정의함으로써 한 단계 더 나아갈 수 있다.
이렇게 하면 구조 인터페이스와 일치하고 자체 map을 구현하는 모든 새로운 모나드도 add의 올림 버전을 즉시 상속받을 것이다.
함수에 필요한 유일한 변경 사항은 타입 시그니처를 일반화하는 것이다.
분석에 유용한 또 다른 모나드 연산자는 모나드 합성(여기서는 중위 >=>로 표현됨)인데, 이는 모나드 함수를 보다 수학적인 스타일로 연결할 수 있도록 한다.
이 연산자를 사용하면 모나드 법칙을 함수만으로 작성할 수 있으며, 결합성 및 항등원의 존재와의 대응을 강조한다.
결과적으로, 위 내용은 하스켈의 "do" 블록의 의미를 보여준다.
모나드 (함수형 프로그래밍) 더 많은 예시
가장 간단한 모나드는 항등 모나드인데, 이는 모나드 법칙을 만족하기 위해 일반 값과 함수에 주석을 달 뿐이다.
Identity는 재귀적인 모나드 트랜스포머의 기저 사례를 제공하는 것과 같이 유효한 사용 사례를 가지고 있다.
또한 명령형 스타일 블록 내에서 기본적인 변수 할당을 수행하는 데 사용될 수도 있다.
적절한 append를 가진 모든 컬렉션은 이미 모노이드이지만, List만이 잘 정의된 join을 가지고 모나드의 자격을 갖춘 컬렉션은 아닌 것으로 밝혀졌다.
append에 특수 속성을 부과하는 것만으로 List를 다른 모나드 컬렉션으로 변형시킬 수도 있다.
모나드 (함수형 프로그래밍) 더 많은 예시
앞서 언급했듯이, 순수 코드는 관리되지 않는 부작용을 가져서는 안 되지만, 프로그램이 효과를 명시적으로 설명하고 관리하는 것을 배제하지는 않는다.
이 아이디어는 하스켈의 IO 모나드의 핵심이다. 여기서 타입 IO a의 객체는 세상에서 수행될 동작을 설명하는 것으로 볼 수 있으며, 선택적으로 타입 a의 세상에 대한 정보를 제공한다. 세상에 대한 정보를 제공하지 않는 동작은 타입 IO ()를 가지며, 더미 값 ()를 "제공"한다.
프로그래머가 IO 값을 함수에 바인딩할 때, 함수는 이전 동작(사용자 입력, 파일 등)이 제공한 세상에 대한 정보를 기반으로 수행될 다음 동작을 계산한다. 가장 중요하게, IO 모나드의 값은 다른 IO 모나드를 계산하는 함수에만 바인딩될 수 있기 때문에, 바인드 함수는 동작의 결과를 수행할 다음 동작을 계산할 함수에만 제공될 수 있는 동작 시퀀스의 규율을 부과한다. 이는 수행할 필요가 없는 동작은 절대 수행되지 않으며, 수행해야 할 동작은 잘 정의된 시퀀스를 갖는다는 것을 의미한다.
모나드 (함수형 프로그래밍) 더 많은 예시
예를 들어, 하스켈에는 파일 시스템에 대한 여러 함수가 있는데, 파일이 존재하는지 확인하는 함수와 파일을 삭제하는 함수가 있다.
두 함수의 타입 시그니처는 다음과 같다.
첫 번째 함수는 주어진 파일이 실제로 존재하는지에 관심이 있으며, 그 결과 IO 모나드 내에서 불리언 값을 출력한다.
두 번째 함수는 파일 시스템에서 작동하는 것에만 관심이 있으므로 출력하는 IO 컨테이너는 비어 있다.
IO는 파일 I/O에만 국한되지 않는다. 사용자 I/O도 허용하며, 명령형 구문 설탕과 함께 일반적인 "Hello, World!" 프로그램을 모방할 수 있다.
탈설탕화하면 다음과 같은 모나드 파이프라인으로 변환된다 (하스켈에서 >>는 모나드 효과만 중요하고 기본 결과는 버릴 수 있을 때의 bind 변형일 뿐이다).
또 다른 일반적인 상황은 로그 파일을 유지하거나 프로그램의 진행 상황을 보고하는 것이다.
때로는 프로그래머가 나중에 프로파일링 또는 디버깅을 위해 더 구체적이고 기술적인 데이터를 로깅하기를 원할 수 있다.
Writer 모나드는 단계별로 누적되는 보조 출력을 생성함으로써 이러한 작업을 처리할 수 있다.
모나드 (함수형 프로그래밍) 더 많은 예시
모나드 패턴이 주로 함수형 언어에만 국한되지 않는다는 것을 보여주기 위해, 이 예제에서는 자바스크립트로 Writer 모나드를 구현한다.
먼저, 배열(중첩된 꼬리 포함)을 사용하면 Writer 타입을 연결 리스트로 구성할 수 있다.
기본 출력 값은 배열의 0번 위치에 있고, 1번 위치는 보조 노트 체인을 암묵적으로 보유한다.
unit을 정의하는 것도 매우 간단하다.
unit만 있으면 디버깅 노트를 포함하는 Writer 객체를 출력하는 간단한 함수를 정의할 수 있다.
진정한 모나드는 여전히 bind를 필요로 하지만, Writer의 경우 이는 단순히 함수의 출력을 모나드의 연결 리스트에 연결하는 것에 해당한다.
이제 샘플 함수는 bind를 사용하여 연결될 수 있지만, 모나드 합성 버전(여기서는 pipelog라고 함)을 정의하면 이러한 함수를 더욱 간결하게 적용할 수 있다.
최종 결과는 계산 단계를 거치는 것과 나중에 감사하기 위해 로깅하는 것 사이의 깔끔한 관심사 분리이다.
모나드 (함수형 프로그래밍) 더 많은 예시
환경 모나드(리더 모나드 및 함수 모나드라고도 함)를 사용하면 계산이 공유 환경의 값에 따라 달라질 수 있다. 모나드 타입 생성자는 타입 T를 함수 타입 E → T에 매핑하며, 여기서 E는 공유 환경의 타입이다. 모나드 함수는 다음과 같다.
다음 모나드 연산은 유용하다.
ask 연산은 현재 컨텍스트를 검색하는 데 사용되며, local은 수정된 하위 컨텍스트에서 계산을 실행한다. 상태 모나드와 마찬가지로 환경 모나드의 계산은 단순히 환경 값을 제공하고 이를 모나드의 인스턴스에 적용하여 호출할 수 있다.
형식적으로, 환경 모나드의 값은 추가적인 익명 인수를 가진 함수와 동일하다. return 및 bind는 SKI 조합자 계산법에서 각각 K 및 S 조합자와 동일하다.
상태 모나드는 프로그래머가 모든 타입의 상태 정보를 계산에 첨부할 수 있도록 한다. 모든 값 타입이 주어졌을 때, 상태 모나드의 해당 타입은 상태를 받아들인 다음 (타입 s의) 새로운 상태와 함께 (타입 t의) 반환 값을 출력하는 함수이다. 이는 환경 모나드와 유사하지만, 새로운 상태를 반환하므로 변경 가능한 환경을 모델링할 수 있다는 점이 다르다.
모나드 (함수형 프로그래밍) 더 많은 예시
이 모나드가 상태 정보의 타입인 타입 매개변수를 취한다는 점에 유의하라. 모나드 연산은 다음과 같이 정의된다.
유용한 상태 연산은 다음과 같다.
다른 연산은 주어진 초기 상태에 상태 모나드를 적용한다.
상태 모나드의 do-블록은 상태 데이터를 검사하고 업데이트할 수 있는 일련의 연산이다.
비공식적으로, 상태 타입 S의 상태 모나드는 반환 값의 타입 T를  타입의 함수로 매핑하며, 여기서 S는 기본 상태이다. return 및 bind 함수는 다음과 같다.
.
범주론적 관점에서, 상태 모나드는 곱 함자와 지수 함자 사이의 수반에서 파생되며, 이는 정의상 모든 데카르트 닫힌 범주에 존재한다.
반환 타입 R을 가진 연속 모나드는 타입 T를 함수 타입 에 매핑한다. 이는 연속 전달 스타일을 모델링하는 데 사용된다. return 및 bind 함수는 다음과 같다.
Call-with-current-continuation 함수는 다음과 같이 정의된다.
다음 코드는 의사 코드이다. 두 함수 foo와 bar가 있다고 가정해 보자. 타입은 다음과 같다.
즉, 두 함수 모두 정수를 받아 다른 정수를 반환한다. 그러면 함수를 다음과 같이 연속적으로 적용할 수 있다.
모나드 (함수형 프로그래밍) 더 많은 예시
결과는 x에 bar를 적용한 결과에 foo를 적용한 결과이다.
그러나 프로그램을 디버깅 중이고 foo와 bar에 로깅 메시지를 추가하고 싶다고 가정해 보자.
그래서 타입을 다음과 같이 변경한다.
그래서 두 함수 모두 튜플을 반환하며, 적용 결과는 정수이고, 적용된 함수와 이전에 적용된 모든 함수에 대한 정보가 포함된 로깅 메시지는 문자열이다.
불행히도, 이는 foo와 bar를 더 이상 구성할 수 없다는 것을 의미한다. 입력 타입 int가 출력 타입 int * string과 호환되지 않기 때문이다. 그리고 각 함수의 타입을 int * string -> int * string으로 수정하여 구성 가능성을 다시 얻을 수 있지만, 이는 튜플에서 정수를 추출하기 위해 각 함수에 상용구 코드를 추가해야 하며, 이러한 함수의 수가 증가함에 따라 지루해질 것이다.
대신, 이 상용구를 추상화하는 도우미 함수를 정의해 보자.
모나드 (함수형 프로그래밍) 더 많은 예시
bind는 정수와 문자열 튜플을 받은 다음, 정수에서 정수와 문자열 튜플로 매핑하는 함수(예: foo)를 받는다. 그 출력은 입력 정수와 문자열 튜플 내의 정수에 입력 함수를 적용한 결과인 정수와 문자열 튜플이다.
이러한 방식으로, bind에서 튜플에서 정수를 추출하는 상용구 코드를 한 번만 작성하면 된다.
이제 우리는 구성 가능성을 어느 정도 회복했다. 예를 들어:
여기서 (x,s)는 정수와 문자열 튜플이다.
이점을 더욱 명확히 하기 위해 bind의 별칭으로 중위 연산자를 정의해 보자.
그래서 t >>= f는 bind t f와 같다.
그러면 위 예시는 다음과 같이 된다.
마지막으로, 빈 로깅 메시지를 생성할 때마다 (x, "")를 작성하지 않도록 새 함수를 정의한다. 여기서 ""는 빈 문자열이다.
이는 위에서 설명한 튜플로 x를 래핑한다.
그 결과는 로깅 메시지를 위한 파이프라인이다.
이를 통해 bar와 foo가 x에 미치는 영향을 더 쉽게 로깅할 수 있다.
int * string은 의사 코드화된 모나드 값을 나타낸다. bind와 return은 같은 이름의 해당 함수와 유사하다.
사실, int * string, bind, return은 모나드를 형성한다.
모나드 (함수형 프로그래밍) 더 많은 예시
가산 모나드는 추가적인 닫힌, 결합적인 이항 연산자 mplus와 mplus 아래의 항등 요소 mzero가 부여된 모나드이다.
Maybe 모나드는 mzero로서 Nothing과 OR 연산자의 변형으로서 mplus를 가진 가산 모나드로 간주될 수 있다.
List 또한 가산 모나드로, 빈 리스트 []는 mzero 역할을 하고 연결 연산자 ++는 mplus 역할을 한다.
직관적으로 mzero는 기본 타입에서 값이 없는 모나드 래퍼를 나타내지만, 모나드 함수에 바인딩될 때마다 mzero를 반환하는 bind의 흡수원 역할을 하므로 "1"이 아닌 "0"으로 간주된다.
이 속성은 양면적이며, 어떤 값이 모나드 영 함수에 바인딩될 때도 bind는 mzero를 반환할 것이다.
범주론적 용어로, 가산 모나드는 bind(모든 모나드가 그렇듯이)를 가진 모나드 함수에 대한 모노이드로서 한 번, 그리고 mplus를 통한 모나드 값에 대한 모노이드로서 다시 한 번 자격을 갖춘다.
모나드 (함수형 프로그래밍) 더 많은 예시
때로는 모나드의 일반적인 윤곽이 유용할 수 있지만, 어떤 모나드가 다른 모나드보다 더 낫다고 추천할 만한 간단한 패턴이 없을 때가 있다.
이것이 자유 모나드가 등장하는 곳이다. 모나드 범주에서 자유 대상으로서, 모나드 법칙을 넘어서는 특정 제약 없이 모나드 구조를 나타낼 수 있다.
자유 모노이드가 평가 없이 요소를 연결하는 것처럼, 자유 모나드는 타입 시스템을 만족시키기 위해 마커와 함께 계산을 연결할 수 있지만, 그 자체로는 더 깊은 의미를 부여하지 않는다.
예를 들어, Just와 Nothing 마커를 통해 전적으로 작동함으로써 Maybe 모나드는 사실 자유 모나드이다.
반면에 List 모나드는 리스트에 대한 추가적이고 구체적인 사실(append와 같은)을 정의에 포함하기 때문에 자유 모나드가 아니다.
마지막 예시는 추상적인 자유 모나드이다.
그러나 자유 모나드는 이 예시처럼 연결 리스트에 국한되지 않으며, 트리와 같은 다른 구조를 중심으로 구축될 수도 있다.
모나드 (함수형 프로그래밍) 더 많은 예시
자유 모나드를 의도적으로 사용하는 것이 처음에는 비실용적으로 보일 수 있지만, 그 형식적인 특성은 구문 문제에 특히 적합하다.
자유 모나드는 나중에 의미론을 남겨두고 구문과 타입을 추적하는 데 사용될 수 있으며, 그 결과 파서와 인터프리터에서 사용되었다.
다른 사람들은 이를 더 동적이고 운영적인 문제에도 적용했으며, 예를 들어 언어 내에서 반복자를 제공하는 데 사용했다.
추가 속성을 가진 모나드를 생성하는 것 외에도, 주어진 모나드에 대해 코모나드를 정의할 수 있다.
개념적으로, 모나드가 기본 값에서 구축된 계산을 나타낸다면, 코모나드는 값으로 다시 환원되는 감소로 볼 수 있다.
모나드 코드는 어떤 의미에서는 완전히 "압축 해제"될 수 없다. 값이 모나드 내부에 래핑되면, 어떤 부작용과 함께 그 안에 격리된 상태로 유지된다 (순수 함수형 프로그래밍에서는 좋은 점이다).
그러나 때로는 문제가 문맥 데이터를 소비하는 것에 더 가깝고, 코모나드는 이를 명시적으로 모델링할 수 있다.
기술적으로, 코모나드는 모나드의 범주론적 쌍대이며, 이는 느슨하게 말하면 타입 시그니처의 방향이 역전된 동일한 필수 구성 요소를 가질 것이라는 의미이다.
bind-중심 모나드 정의에서 시작하여, 코모나드는 다음으로 구성된다.
extend 및 counit은 또한 모나드 법칙의 쌍대도 만족해야 한다.
모나드와 마찬가지로 코모나드도 join의 쌍대를 사용하여 함자에서 파생될 수 있다.
그러나 extend와 같은 연산은 역전되지만, 코모나드는 그에 작용하는 함수를 역전시키지 않으며, 결과적으로 코모나드는 여전히 map을 가진 함자이지 공함자는 아니다.
duplicate, counit, map을 사용한 대체 정의도 자체 코모나드 법칙을 준수해야 한다.
모나드와 마찬가지로 두 형식은 자동으로 변환될 수 있다.
간단한 예시는 곱 코모나드이며, 입력 값과 공유 환경 데이터를 기반으로 값을 출력한다.
사실, Product 코모나드는 Writer 모나드의 쌍대이며 Reader 모나드와 실질적으로 동일하다 (둘 다 아래에서 논의됨).
Product와 Reader는 수용하는 함수 시그니처와 값 래핑 또는 언래핑을 통해 해당 함수를 보완하는 방식에서만 다르다.
덜 자명한 예는 스트림 코모나드인데, 이는 데이터 스트림을 나타내고 extend를 사용하여 들어오는 신호에 필터를 첨부하는 데 사용될 수 있다.
사실, 모나드만큼 인기가 있지는 않지만, 연구자들은 코모나드가 스트림 프로세싱 및 데이터플로 프로그래밍 모델링에 특히 유용하다는 것을 발견했다.
그러나 엄격한 정의 때문에 모나드와 코모나드 사이에서 객체를 단순히 앞뒤로 이동할 수는 없다.
더 높은 추상화로서 화살은 두 구조를 모두 포괄할 수 있지만, 모나드 및 코모나드 코드를 결합하는 더 세밀한 방법을 찾는 것은 활발한 연구 분야이다.
Magisk
Magisk는 존 우가 개발한 안드로이드 기기의 사용자 공간 시스템리스 루팅을 위한 자유-오픈 소스 소프트웨어이다. Magisk는 안드로이드 6.0 이상을 실행하는 기기를 지원한다.
Magisk 개요
Magisk는 사용자가 안드로이드 기기에 루팅 권한을 얻을 수 있게 해주는 자유-오픈 소스 소프트웨어이다. Magisk를 사용하면 다양한 수정 및 사용자 지정을 설치할 수 있어 안드로이드 애호가들에게 인기가 많다. 또한 Magisk는 내장된 Magisk Manager 앱과 함께 제공되어 사용자가 루팅 권한을 관리하고 다양한 모듈을 설치할 수 있게 한다.
Magisk는 모듈식 디자인을 가지고 있으며, 시스템리스 접근 방식을 위해 매직 마운트를 사용한다. 안드로이드 기기를 루팅하여 새로운 기능과 기능을 추가하는 안전하고 쉬운 방법을 제공한다.
Magisk 작동 방식
사용자는 펌웨어 (init_boot.img 또는 boot.img)를 다운로드하고 Magisk를 사용하여 파일을 패치한다. 부트로더 잠금 해제 후, 패치된 파일은 패스트부트 또는 오딘 (삼성 기기용)을 사용하여 플래시된다. 이렇게 함으로써 Magisk는 안드로이드 기기의 부팅 과정에서 Init을 magiskinit으로 대체하여 루팅 권한을 얻는다.
Magisk 역사
Magisk는 존 우가 만든 작은 프로젝트로 시작되었다. 그러나 지금은 252명 이상의 기여자들로 성장했다. 버전 21에서는 안드로이드 11에 대한 지원이 추가되었다. 버전 22에서는 삼성 갤럭시 S21에 대한 지원이 추가되었다. 버전 26에서는 안드로이드 5 이하 버전에 대한 지원이 제거되었다. 원래 개발자인 존 우는 2021년 5월에 안드로이드 보안팀에서 일하기 시작했다.
레드부트
레드부트(RedBoot, Red Hat Embedded Debug and Bootstrap firmware' 두문자어)는 eCos 실시간 운영체제 하드웨어 추상화 계층을 사용하여 임베디드 시스템을 위한 부트스트랩 펌웨어를 제공하는 오픈 소스 응용 소프트웨어이다.
레드부트는 임베디드 리눅스 및 eCos 응용 프로그램을 포함하여 직렬 또는 이더넷을 통해 임베디드 응용 프로그램을 다운로드하고 실행할 수 있도록 한다. GDB와 연동하여 임베디드 응용 프로그램의 개발 및 디버깅을 지원한다. 또한 직렬 또는 이더넷을 통해 접근할 수 있는 대화형 명령 줄 인터페이스를 제공하여 플래시 이미지, 이미지 다운로드, 레드부트 구성 등을 관리할 수 있도록 한다. 무인 또는 자동 시작을 위해 부트 스크립트를 플래시에 저장하여 플래시, 하드 디스크 또는 TFTP 서버에서 이미지를 로드할 수 있다.
EFI 시스템 파티션
EFI(Extensible Firmware Interface) 시스템 파티션 또는 ESP는 UEFI를 사용하는 컴퓨터에서 이용되는 데이터 저장 장치(일반적으로 하드 디스크 드라이브 또는 솔리드 스테이트 드라이브)의 파티션이다. 컴퓨터가 부팅될 때, UEFI 펌웨어는 ESP에 저장된 파일을 로드하여 운영체제와 다양한 유틸리티를 시작한다.
ESP에는 설치된 운영체제(부트로더, 부트 관리자, 커널 이미지)와 컴퓨터에 존재하는 하드웨어 장치에 대한 장치 드라이버 파일 및 부팅 시 펌웨어에서 사용되는 시스템 유틸리티 프로그램과 오류 로그와 같은 데이터 파일이 포함된다.
EFI 시스템 파티션 개요
EFI 시스템 파티션은 UEFI 사양의 일부로 유지 관리되는 FAT 파일 시스템을 기반으로 하는 파일 시스템으로 포맷된다. 따라서 파일 시스템 사양은 원래 FAT 사양과 독립적이다. 실제 차이의 정도는 알려져 있지 않다. 애플은 인텔/X86-64 맥에서 사용해야 하는 별도의 도구를 유지 관리하고 있지만, 다른 시스템에서는 FAT 유틸리티를 잘 사용한다. GUID 파티션 테이블 (GPT) 방식에서 EFI 시스템 파티션의 전역 고유 식별자 (GUID)는 C12A7328-F81F-11D2-BA4B-00A0C93EC93B이고, 마스터 부트 레코드 (MBR) 파티션 테이블 방식에서의 ID는 0xEF이다. UEFI 펌웨어는 두 파티션 방식 모두를 지원해야 하므로, GPT 및 MBR로 파티션된 디스크 모두 EFI 시스템 파티션을 포함할 수 있다. 또한 엘 토리토 CD-ROM 및 DVD용 부팅 가능 형식도 지원된다.