ความแตกต่างระหว่าง `data 'และ` newtype` ใน Haskell


191

เมื่อฉันเขียนสิ่งนี้แตกต่างกันอย่างไร

data Book = Book Int Int

กับ

newtype Book = Book (Int, Int) -- "Book Int Int" is syntactically invalid

คุณควรทำการค้นหารอบ ๆ คำถามนี้ได้รับคำตอบแล้ว stackoverflow.com/questions/2649305/…
tehman

เกี่ยวข้องกับstackoverflow.com/questions/2649305/…
Don Stewart

สิ่งที่เกี่ยวข้อง: ใช้สำหรับ newtype: stackoverflow.com/questions/991467/…
Don Stewart

25
โปรดทราบว่าnewtype Book = Book Int Intไม่ถูกต้อง อย่างไรก็ตามคุณสามารถมีnewtype Book = Book (Int, Int)ดังที่ดอนด้านล่าง
Edward KMETT

คำตอบ:


241

เป็นคำถามที่ดีมาก!

มีความแตกต่างที่สำคัญหลายประการ

การแสดง

  • A newtypeรับประกันว่าข้อมูลของคุณจะมีการแสดงที่เหมือนกับรันไทม์เหมือนกับชนิดที่คุณตัดคำ
  • ในขณะที่dataประกาศโครงสร้างข้อมูลใหม่ที่รันไทม์

ดังนั้นจุดสำคัญที่นี่คือการสร้างสำหรับnewtypeรับประกันจะถูกลบในเวลารวบรวม

ตัวอย่าง:

  • data Book = Book Int Int

ข้อมูล

  • newtype Book = Book (Int, Int)

Newtype

สังเกตว่ามันมีการแสดงเหมือนกันทุกประการเหมือนกับ a (Int,Int)เนื่องจากตัวBookสร้างถูกลบทิ้ง

  • data Book = Book (Int, Int)

data tuple

มีเพิ่มเติมคอนสตรัคไม่ได้อยู่ในBooknewtype

  • data Book = Book {-# UNPACK #-}!Int {-# UNPACK #-}!Int

ป้อนคำอธิบายรูปภาพที่นี่

ไม่มีตัวชี้! Intฟิลด์สองฟิลด์เป็นฟิลด์ขนาดคำที่ไม่ได้กำหนดไว้ในตัวBookสร้าง

ชนิดข้อมูลเชิงพีชคณิต

เพราะความจำเป็นนี้เพื่อลบตัวสร้างการnewtypeทำงานเฉพาะเมื่อห่อชนิดข้อมูลกับนวกรรมิกเดียว ไม่มีความคิดเกี่ยวกับ newtypes "พีชคณิต" นั่นคือคุณไม่สามารถเขียนรูปแบบใหม่ที่เทียบเท่าพูดได้

data Maybe a = Nothing
             | Just a

เนื่องจากมีคอนสตรัคเตอร์มากกว่าหนึ่งตัว คุณไม่สามารถเขียน

newtype Book = Book Int Int

ความเข้มงวด

ความจริงที่ว่าคอนสตรัคจะถูกลบออกนำไปสู่ความแตกต่างบางอย่างในระหว่างเข้มงวดและdata newtypeโดยเฉพาะอย่างยิ่งdataแนะนำประเภทที่ "ยก" ความหมายเป็นหลักว่ามันมีวิธีการเพิ่มเติมในการประเมินมูลค่าด้านล่าง เนื่องจากไม่มีคอนสตรัคเตอร์เพิ่มเติมขณะใช้งานด้วยnewtypeคุณสมบัตินี้จึงไม่เก็บ

ตัวชี้พิเศษในBookto to (,)constructor ช่วยให้เราสามารถใส่ค่าล่าง

เป็นผลให้newtypeและdataมีคุณสมบัติในการเข้มงวดแตกต่างกันเล็กน้อยในขณะที่อธิบายไว้ในบทความวิกิพีเดีย Haskell

unboxing

มันไม่สมเหตุสมผลที่จะแกะส่วนประกอบของ a newtypeออกเนื่องจากไม่มีคอนสตรัคเตอร์ ในขณะที่มันมีเหตุผลอย่างสมบูรณ์แบบที่จะเขียน:

data T = T {-# UNPACK #-}!Int

ยอมให้วัตถุรันไทม์ที่มีคอนTสตรัคเตอร์และInt#ส่วนประกอบ คุณเพียงแค่ได้รับเปลือยด้วยIntnewtype


การอ้างอิง :


2
ฉันยังไม่คิดว่าฉันจะพลาดบางสิ่งบางอย่างหากไม่มี "newtype" ใน Haskell ความแตกต่างที่ลึกซึ้งเพิ่มความซับซ้อนให้เป็นภาษาที่ดูเหมือนจะไม่ worthwile ฉัน ...
martingw

14
ความแตกต่างนั้นมีประโยชน์มากสำหรับเหตุผลด้านประสิทธิภาพ เนื่องจากตัวสร้างชนิดใหม่ถูกลบในเวลาคอมไพล์พวกเขาไม่ได้กำหนดบทลงโทษประสิทธิภาพรันไทม์ที่ตัวสร้างข้อมูลทำ แต่พวกเขายังคงให้ประโยชน์ทั้งหมดของประเภทที่แตกต่างอย่างสมบูรณ์และสิ่งที่เป็นนามธรรมที่คุณต้องการเชื่อมโยงกับมัน ตัวอย่างเช่นมีสองวิธีที่แตกต่างกันที่ชนิดข้อมูลรายการสามารถสร้าง monad ได้ หนึ่งถูกสร้างขึ้นในภาษา แต่ถ้าคุณต้องการที่จะใช้อีกคนหนึ่งชนิดใหม่จะเป็นวิธีที่จะไป
mightybyte

คำอธิบายที่ดี! สิ่งที่ฉันไม่เข้าใจคือถ้าnewtypeถูกลบหลังจากการคอมไพล์และรันไทม์ใช้การแทนแบบเดียวกันสำหรับชนิดเก่าและใหม่เราจะยังสามารถกำหนดอินสแตนซ์สำหรับทั้งเก่าและใหม่ได้อย่างไร รันไทม์สามารถเข้าใจอินสแตนซ์ที่ใช้อย่างไร
damluar

3
@damluar ทุกประเภทจะถูกลบที่ runtime พวกมันทั้งหมดได้รับการแก้ไขอย่างสมบูรณ์ในเวลารวบรวมและในระหว่างการคอมไพล์newtypeก็ยังไม่ถูกลบ
อัฒภาค

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