“ concatMap” จาก mono-traversable นั้นสามารถ“ ดึงออก” อาร์กิวเมนต์ทั่วไปได้อย่างไร


9

ฉันกำลังเรียนรู้ Haskell และกำลังทำโปรแกรมฐานข้อมูลอย่างง่ายสำหรับ Yesod เมื่อฉันสะดุดกับพฤติกรรมนี้ซึ่งฉันเข้าใจยาก

testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []

Yesod GHCI เซสชั่น:

$ :t concatMap testFn [3]
concatMap testFn [3] :: Bool -> [Int]
$ (concatMap testFn [1,2,3]) True
[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3]

อย่างใดก็สามารถ "ดึง" ออก "Bool" ที่สองจากการแมปแต่ละครั้งเป็นอาร์กิวเมนต์ curated เดียว

โหมโรง GHCI พื้นฐานแบบมาตรฐานปฏิเสธที่จะคอมไพล์นิพจน์นี้:

$ :t concatMap testFn [3]
error:
     Couldn't match type 'Bool -> [Int]' with '[b]'
      Expected type: Int -> [b]
        Actual type: Int -> Bool -> [Int]
     Probable cause: 'testFn' is applied to too few arguments
      In the first argument of 'concatMap', namely 'testFn'
      In the expression: concatMap testFn [3]

ปรากฎว่า Yesod ใช้ไลบรารีแบบ mono-traversableซึ่งมีconcatMap:

$ :t concatMap
concatMap
  :: (MonoFoldable mono, Monoid m) =>
     (Element mono -> m) -> mono -> m

ที่ระดับปัจจุบันของฉันในการทำความเข้าใจแฮสเค็ลล์ฉันไม่สามารถทราบได้ว่าจะกระจายประเภทอย่างไรที่นี่ ใครสามารถอธิบายให้ฉัน (เริ่มต้นที่มุ่งเน้นมากที่สุดเท่าที่เป็นไปได้) วิธีการทำเคล็ดลับนี้? ส่วนใดของtestFnข้างต้นที่สอดคล้องกับElement monoประเภท

คำตอบ:


6

เราเริ่มต้นด้วยการแสดงรายการบางประเภทที่เรารู้ (เราแกล้งทำเป็นว่าตัวเลขมีIntไว้เพื่อความเรียบง่าย - มันไม่เกี่ยวข้องกันจริงๆ)

testFn :: Int -> Bool -> [Int]
[1,2,3] :: [Int]
True :: Bool

(concatMap testFn [1,2,3]) TrueเหมือนกันconcatMap testFn [1,2,3] Trueดังนั้นconcatMapจะต้องมีประเภทที่ตรงกับข้อโต้แย้งทั้งหมด:

concatMap :: (Int -> Bool -> [Int]) -> [Int] -> Bool -> ???

ที่???เป็นประเภทผล โปรดทราบว่าเนื่องจากกฎ->ความสัมพันธ์เชื่อมโยงไปทางขวาดังนั้นการพิมพ์ด้านบนจึงเหมือนกับ:

concatMap :: (Int -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)

ลองเขียนประเภททั่วไปข้างบนอันนั้น ฉันกำลังเพิ่มช่องว่างเล็กน้อยเพื่อทำเครื่องหมายความคล้ายคลึงกัน

concatMap :: (MonoFoldable mono, Monoid m) =>
             (Element mono -> m              ) -> mono  -> m
concatMap :: (Int          -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)

อ๋อ! เรามีการแข่งขันถ้าเราเลือกmเป็นBool -> [Int]และเป็นmono [Int]หากเราทำเช่นนั้นเราจะปฏิบัติตามข้อ จำกัดMonoFoldable mono, Monoid m(ดูด้านล่าง) และเราก็มีElement mono ~ Intดังนั้นทุกอย่างจะตรวจสอบประเภท

เราอนุมานได้ว่า???เป็นจากความหมายของ[Int]m

เกี่ยวกับข้อ จำกัด : สำหรับMonoFoldable [Int]มีเล็กน้อยที่จะพูด [Int]เห็นได้ชัดว่าเป็นรายการที่เหมือนชนิดที่มีIntประเภทองค์ประกอบและที่มากพอที่จะทำให้มันเป็นMonaFoldableที่มีฐานะIntElement

สำหรับMonoid (Bool -> [Int])มันซับซ้อนกว่าเล็กน้อย เรามีฟังก์ชั่นใด ๆ ที่A -> Bเป็นแบบ monoid ถ้าBเป็น monoid สิ่งนี้ตามมาด้วยการปฏิบัติงานในลักษณะที่เป็นจุด ในกรณีเฉพาะของเราเราพึ่งพา[Int]การเป็น monoid และเราได้:

mempty :: Bool -> [Int]
mempty = \_ -> []

(<>) :: (Bool -> [Int]) -> (Bool -> [Int]) -> (Bool -> [Int])
f <> g = \b -> f b ++ g b

1
คำอธิบายที่ดี! วิธีที่คุณอธิบายและจัดประเภทเป็นเพียงสิ่งที่ฉันต้องการดูขอบคุณ!
dimsuz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.