함수에 대한 타입 클래스가없는 이유는 무엇입니까?


17

내가 겪고있는 학습 문제에서 적용, 작성 등의 작업을 수행하는 함수에 대한 typeclass가 필요하다는 것을 깨달았습니다.

  1. 함수의 표현을 함수 자체처럼 취급하는 것이 편리하므로 함수를 암시 적으로 해석하는 인터프리터를 사용하고 함수를 작성하면 새로운 설명이 도출됩니다.

  2. 함수에 대한 유형 클래스가 있으면 특수한 종류의 함수에 대한 유형 클래스를 파생시킬 수 있습니다. 필자의 경우에는 뒤집을 수없는 함수가 필요합니다.

예를 들어 정수 오프셋을 적용하는 함수는 정수를 포함하는 ADT로 나타낼 수 있습니다. 이러한 함수를 적용한다는 것은 정수를 추가하는 것을 의미합니다. 랩핑 된 정수를 추가하여 구성이 구현됩니다. 역함수는 정수를 부정합니다. identity 함수는 0을 래핑합니다. 상수 함수는 적절한 표현이 없기 때문에 제공 할 수 없습니다.

물론 값이 Haskell의 진정한 기능인 것처럼 철자를 쓸 필요는 없지만 일단 아이디어를 얻은 후에는 이와 같은 라이브러리가 이미 존재하고 표준 철자를 사용한다고 생각했습니다. 그러나 Haskell 라이브러리에서 이러한 유형 클래스를 찾을 수 없습니다.

Data.Function 모듈을 찾았 지만 typeclass 가 없습니다. Prelude에서도 사용할 수있는 몇 가지 일반적인 함수 만 있습니다.

그렇다면 함수형 클래스가없는 이유는 무엇입니까? "그렇지 않아서"또는 "생각이 그렇게 유용하지 않기 때문에"입니까? 아니면 아이디어에 근본적인 문제가 있습니까?

지금까지 내가 생각한 가장 큰 문제는 루핑 문제를 피하기 위해 실제 함수의 함수 응용 프로그램이 컴파일러에 의해 특수 사례되어야한다는 것입니다.이 함수를 적용하려면 함수 응용 프로그램 함수를 적용해야합니다. 그렇게하려면 함수 응용 프로그램 함수를 호출해야합니다.

더 많은 단서

내가 목표로하는 것을 보여주는 예제 코드 ...

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}

--  In my first version, Doable only had the one argument f. This version
--  seemed to be needed to support the UndoableOffset type.
--
--  It seems to work, but it also seems strange. In particular,
--  the composition function - a and b are in the class, but c isn't,
--  yet there's nothing special about c compared with a and b.
class Doable f a b where
  fwdApply :: f a b -> a -> b
  compDoable :: f b c -> f a b -> f a c

--  In the first version, I only needed a constraint for
--  Doable f a b, but either version makes sense.
class (Doable f a b, Doable f b a) => Undoable f a b where
  bwd      :: f a b -> f b a

  bwdApply :: f a b -> b -> a
  bwdApply f b = fwdApply (bwd f) b

--  Original ADT - just making sure I could wrap a pair of functions
--  and there were no really daft mistakes.
data UndoableFn a b = UFN { getFwd :: a -> b, getBwd :: b -> a }

instance Doable UndoableFn a b where
  fwdApply = getFwd
  compDoable f g = UFN ((getFwd f) . (getFwd g)) ((getBwd g) . (getBwd f))

instance Undoable UndoableFn a b where
  bwd f    = UFN (getBwd f) (getFwd f)
  bwdApply = getBwd

--  Making this one work led to all the extensions. This representation
--  can only represent certain functions. I seem to need the typeclass
--  arguments, but also to need to restrict which cases can happen, hence
--  the GADT. A GADT with only one constructor still seems odd. Perhaps
--  surprisingly, this type isn't just a toy (except that the whole thing's
--  a toy really) - it's one real case I need for the exercise. Still a
--  simple special case though.
data UndoableOffset a b where
  UOFF :: Int -> UndoableOffset Int Int

instance Doable UndoableOffset Int Int where
  fwdApply (UOFF x) y = y+x
  compDoable (UOFF x) (UOFF y) = UOFF (x+y)

instance Undoable UndoableOffset Int Int where
  bwdApply (UOFF x) y = y-x
  bwd (UOFF x) = UOFF (-x)

--  Some value-constructing functions
--  (-x) isn't shorthand for subtraction - whoops.
undoableAdd :: Int -> UndoableFn Int Int
undoableAdd x = UFN (+x) (\y -> y-x)

undoableMul :: Int -> UndoableFn Int Int
undoableMul x = UFN (*x) (`div` x)

--  With UndoableFn, it's possible to define an invertible function
--  that isn't invertible - to break the laws. To prevent that, need
--  the UFN constructor to be private (and all public ops to preserve
--  the laws). undoableMul is already not always invertible.
validate :: Undoable f a b => Eq a => f a b -> a -> Bool
validate f x = (bwdApply f (fwdApply f x)) == x

--  Validating a multiply-by-zero invertible function shows the flaw
--  in the validate-function plan. Must try harder.
main = do putStrLn . show $ validate (undoableAdd 3) 5
          putStrLn . show $ validate (undoableMul 3) 5
          --putStrLn . show $ validate (undoableMul 0) 5
          fb1 <- return $ UOFF 5
          fb2 <- return $ UOFF 7
          fb3 <- return $ compDoable fb1 fb2
          putStrLn $ "fwdApply fb1  3 = " ++ (show $ fwdApply fb1  3)
          putStrLn $ "bwdApply fb1  8 = " ++ (show $ bwdApply fb1  8)
          putStrLn $ "fwdApply fb3  2 = " ++ (show $ fwdApply fb3  2)
          putStrLn $ "bwdApply fb3 14 = " ++ (show $ bwdApply fb3 14)

이 응용 프로그램에는 통합 값이 같지 않은 프롤로그 스타일 논리가 a = f(b)아닌 제약 조건 과 같은 불가역 함수를 통해 관련되는 일종의 통일이 포함됩니다 a = b. 구성의 대부분은 노조 찾기 구조를 최적화하여 생성됩니다. 역수의 필요성은 분명해야합니다.

통합 집합의 항목에 정확한 값이없는 경우 해당 통합 집합의 다른 항목에 대해서만 특정 항목을 수량화 할 수 있습니다. 이것이 바로 "실제"기능을 사용하고 싶지 않은 이유입니다. 나는 전체 기능 측면을 떨어 뜨릴 수 있고 절대적이고 상대적인 양을 가질 수 있습니다. 아마도 숫자 / 벡터 만 필요할 수도 있습니다. (+)그러나 내 내부 건축 우주 비행사는 그의 재미를 원합니다.

링크를 다시 분리하는 유일한 방법은 역 추적을 통하는 것이며 모든 것이 순수합니다. 조합 찾기는 키를 IntMap"포인터"로 사용하여 수행됩니다 . 나는 단순한 노동 조합 찾기 작업을했지만 아직 뒤집을 수없는 기능을 추가하지 않았으므로 여기에 열거 할 요점이 없습니다.

Applicative, Monad, Arrow 등을 사용할 수없는 이유

내가 제공하는 함수 추상화 클래스가 필요한 주요 작업은 응용 프로그램 및 구성입니다. 친숙하게 들립니다. 예를 들어 Applicative (<*>), Monad (>>=)그리고 Arrow (>>>)모든 컴포지션 기능입니다. 그러나 필자의 경우 함수 추상화를 구현하는 형식에는 함수를 나타내지 만 함수를 포함 할 수 없으며 포함 할 수없는 일부 데이터 구조가 포함되어 있으며 제한된 함수 집합 만 나타낼 수 있습니다.

코드 설명에서 언급했듯이 "통합"클러스터의 항목에 정확한 값이 없기 때문에 한 항목을 다른 항목에 대해서만 수량화 할 수 있습니다. 나는 그 함수의 표현을 이끌어 내고자한다. 일반적으로 제공된 여러 함수 (조합 / 찾기 트리에서 공통 조상까지 걸어 가기)와 여러 역함수 (다른 것으로 내려 가기)로 구성 될 것이다. 안건).

간단한 경우-원래의 "함수"가 정수 오프셋 "함수"로 제한되는 경우 정수 오프셋 "함수"로 구성된 결과를 원합니다-구성 요소 오프셋을 추가하십시오. 이것이 컴포지션 함수가 응용 프로그램 함수뿐만 아니라 클래스에 있어야하는 이유의 큰 부분입니다.

이 방법은 내가 작업을 제공 할 수없는 pure, return또는 arr내 유형, 내가 사용할 수 있도록 Applicative, Monad또는 Arrow.

이것은 이러한 유형의 실패가 아니며 추상화의 불일치입니다. 내가 원하는 추상화는 단순한 순수한 기능입니다. 예를 들어 부작용이 없으며 모든 기능에 적용되는 표준 (.)과 다른 기능을 시퀀싱하고 구성하기위한 편리한 표기법을 작성할 필요가 없습니다.

나는 인스턴스 할 수Category 있습니다. 나는 필요하지는 않지만 모든 기능적인 것들이 정체성을 제공 할 수 있다고 확신합니다. 그러나 Category응용 프로그램을 지원하지 않기 때문에 여전히 해당 작업을 추가하려면 파생 클래스가 필요합니다.


2
나를 미치게 부르십시오. 그러나 당신이 묘사하고있는 것과 같은 타입 클래스를 생각할 때 적용하고 작성하는 등의 기능을합니다. 아마도 그것은 당신이 생각하고있는 유형 클래스입니까?
Jimmy Hoffa

1
나는 Applicative옳지 않다고 생각 합니다-함수뿐만 아니라 값도 줄 바꿈해야하지만 함수 만 줄 바꿈하고 감싸 인 함수는 실제로 함수이지만 랩 된 함수는 일반적으로 (in 가장 일반적인 경우는 기능을 설명하는 AST입니다). 어디 <*>유형이 f (a -> b) -> f a -> f b나는 유형의 응용 프로그램 운영자 원하는 및 도메인을 지정하고 포장 기능의 공역을,하지만 래퍼 안에 것은 없습니다 (필요) 진정한 기능입니다. 애로 우즈 (Arrows)-아마도, 내가 볼 것이다. g a b -> a -> bab
Steve314

3
역을 원한다면 그룹을 의미하지 않습니까?
jk.

1
@jk. 중요한 점은 OP가 자신이 찾고있는 것을 찾도록 이끌 수있는 함수의 역에 대해 읽을 것들이 많이 있다는 것을 깨달았습니다. 여기 에 주제에 대한 흥미로운 독서가 있습니다. 그러나 구글 for haskell 함수 역은 호기심 많은 독서 콘텐츠를 제공합니다. 아마도 그는 단지 Data.Group을 원할 것입니다
Jimmy Hoffa

2
@ Steve314 작곡 기능이 단일 범주라고 생각했습니다. 도메인과 코 도메인이 항상 같은 경우 단일 ID입니다.
Tim Seguine

답변:


14

글쎄, 나는 "함수 -y"를 나타내는 것으로 스스로 시장에 내놓아 진 아이디어에 대해 잘 모른다. 그러나 가까이 오는 몇 가지가 있습니다

카테고리

정체성과 구성이있는 간단한 기능 개념이 있다면 범주보다 있습니다.

class Category c where
  id :: c a a
  (.) :: c b c -> c a b -> c a c

단점은 당신이 개체의 집합 멋진 카테고리의 인스턴스를 만들 수 있다는 것입니다 ( a, b, 및 c). 내가 생각하는 맞춤 카테고리 클래스를 만들 수 있습니다.

화살표

함수에 제품 개념이 있고 임의의 함수를 삽입 할 수있는 경우 화살표가 사용자를위한 것보다

 class Arrow a where
   arr :: (b -> c) -> a b c
   first :: a b c -> a (b, d) (c, d)
   second :: a b c -> a (d, b) (d, c)

ArrowApply 원하는 것에 중요한 응용 프로그램 개념이 있습니다.

응용

응용 프로그램은 응용 프로그램에 대한 개념을 가지고 있으며, AST에서 함수 응용 프로그램을 나타내는 데 사용했습니다.

class Functor f => Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f b -> f c

다른 많은 아이디어가 있습니다. 그러나 일반적인 주제는 함수를 나타내는 일부 데이터 구조를 작성하여 해석 함수에 전달하는 것입니다.

이것은 또한 얼마나 많은 무료 모나드가 작동하는지입니다. 나는 당신이 용감하다고 느끼면 이것들을 찌를 것을 제안 할 것입니다, 그들은 당신이 제안하는 것들을위한 강력한 도구이며, do표기법을 사용하여 데이터 구조를 구축 한 다음 다른 기능으로 계산에 영향을 미치는 측면으로 축소 할 수 있습니다 . 그러나 이러한 기능은 데이터 구조에서만 작동하며 실제로 어떻게 만들 었는지 알지 못한다는 것이 장점입니다. 이것이 당신의 통역사에 대한 제안입니다.


응용 프로그램이 부족한 것 같습니다- ($). 화살은 첫눈에 엄청난 과잉처럼 보이지만 여전히 ArrowApply유망한 것처럼 보입니다. 내가 할 수없는 것을 제공 할 필요가없는 한 괜찮습니다. 더 많은 점검이 필요한 순간 ​​+1
Steve314

3
Steve314 카테고리가 부족 신청을 해 있지만, 모나드가 실행할 수있는 보편적 인 방법이 부족 @, 그들이 유용하지 않은 것을 의미하지 않는다
다니엘 Gratzer

내가 사용 할 수없는 이유 공통의 이유가 Applicative또는 Arrow(나 Monad) - 내 타입의 값이 때문에 내가 (일반적으로) 정상적인 기능을 포장 할 수없는 표현 하는 기능 만하는 표현 데이터로, 임의의 기능을 지원하지 않습니다이 경우 경우 번역하는 방법이있었습니다. 즉 pure, arr또는을 ( return를) 제공 할 수 없습니다 . BTW-해당 클래스는 유용하지만이 특정 목적으로 사용할 수 없습니다. Arrow"대단한 과잉 살상"이 아닙니다. 그것은 내가 신문을 이해할 준비가되지 않았을 때 마지막으로 신문을 읽으려고했을 때의 잘못된 인상이었습니다.
Steve314

Steve314 @ 데이터를 구축하는 모나드 인터페이스를 제공하는 아이디어는 무료 모나드가 그들을 밖으로 검사에 사용되는 것입니다
다니엘 Gratzer을

나는 Haskell Exchange 2013의 비디오를 보았습니다. Andres Löh는 분명히 잘 설명하지만 여전히 다시 볼 필요가 있고 기술 등을 가지고 놀아야합니다. 그래도 여기에 필요한지는 확실하지 않습니다. 내 목표는 함수가 아닌 (그러나 통역사 기능이있는) 표현을 사용하여 함수를 추상화하는 것입니다. 부작용 추상화가 필요하지 않으며 시퀀싱 작업에 대한 명확한 표기법이 필요하지 않습니다. 이 함수 추상화가 사용되면 응용 프로그램 및 구성은 한 번에 하나씩 다른 라이브러리의 알고리즘 내에서 수행됩니다.
Steve314

2

당신이 지적했듯이, 여기서 Applicative를 사용할 때의 주요 문제는에 대한 합리적인 정의가 없다는 것입니다 pure. 따라서 Apply발명되었다. 적어도 내 이해입니다.

불행히도, 나는 Apply그 인스턴스가 아닌 예제를 가지고 있지 않습니다 Applicative. 이것이 사실이라고 주장 IntMap하지만 이유를 모르겠습니다. 마찬가지로, 예제-오프셋 정수- Apply인스턴스를 허용하는지 여부를 모르겠습니다 .


이것은 댓글처럼 더 읽습니다. 답변하는 방법
gnat

죄송합니다. 이것은 제 첫 대답입니다.
user185657

답변을 개선 할 것을 제안하는 방법은 무엇입니까?
user185657

고려 편집 도움말 독자에게 보내고 응답 주소가 질문, 질문 방법을 볼 당신이 생각하는 것처럼 그렇게 유용하지 때문에이 없다 "또는"해서 ""왜 기능을위한 typeclass가없는? 그것은인가? "아니면 아이디어에 근본적인 문제가 있습니까? "
gnat

1
이것이 더
낫기를

1

언급 이외에 Category, ArrowApplicative:

Data.LambdaConal Elliott 도 발견 했습니다.

람다와 같은 구성을 가진 일부 함수와 유사한 클래스

물론 흥미로워 보이지만 예제없이 이해하기는 어렵습니다 ...

위키 페이지 에서 라이브러리 생성을 유발 한 것 중 하나 인 유형 가치 (TV) 에 대한 예를 찾을 수 있습니다 TypeCompose. 입력 및 함수 값 출력을 참조하십시오 .

TV 라이브러리의 아이디어는 하스켈 값 (함수 포함)을 확실한 방식으로 표시하는 것입니다.

베어 론크를 게시하지 않는 것에 대한 StackOverflow 규칙을 따르기 위해 아래에 약간의 비트를 복사하여 다음과 같은 아이디어를 제공합니다.

첫 번째 예는 다음과 같습니다.

apples, bananas :: CInput Int
apples  = iTitle "apples"  defaultIn
bananas = iTitle "bananas" defaultIn

shoppingO :: COutput (Int -> Int -> Int)
shoppingO = oTitle "shopping list" $
            oLambda apples (oLambda bananas total)

shopping :: CTV (Int -> Int -> Int)
shopping = tv shoppingO (+)

다음과 같이 실행될 때 제공됩니다 runIO shopping(더 많은 주석, GUI 및 더 많은 예제는 여기 참조).

shopping list: apples: 8
bananas: 5
total: 13

이 질문은 어떻게 질문을 해결합니까? 보고 답변하는 방법
모기

@ gnat 나는 정의가 Data.Lambda함수와 같은 것들 (요청 된)에 대한 클래스를 제공 한다고 생각했다 . 나는 이것을 조금 탐구했다. 아마도 함수 응용 프로그램에 대한 추상화를 제공하지는 않습니다.
imz-Ivan Zakharyaschev 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.