ميزات هاسكل

تتناول هذه المقالة خصائص لغة البرمجة هاسكل .

أمثلة

مضروب

أحد الأمثلة البسيطة التي تُستخدم غالبًا لتوضيح بنية اللغات الوظيفية هو دالة حساب المضروب للأعداد الصحيحة غير السالبة، كما هو موضح في لغة هاسكل:

دالة المضروب :: عدد صحيح -> عدد صحيح ، مضروب 0 = 1 ، مضروب ن = ن * مضروب ( ن - 1 )

أو في سطر واحد:

مضروب n = إذا كان n > 1 فإن n * مضروب ( n - 1 ) وإلا 1

يصف هذا الوصف المضروب كدالة تكرارية، ذات حالة توقف واحدة. وهو مشابه لوصف المضروب الموجود في كتب الرياضيات. يتشابه جزء كبير من كود هاسكل مع الترميز الرياضي القياسي من حيث التسهيل والبنية.

يصف السطر الأول من دالة المضروب نوع هذه الدالة؛ ورغم أنه اختياري، إلا أنه يُعتبر أسلوبًا برمجيًا جيدًا [ 1 ] . ويمكن قراءته كالتالي: دالة المضروب ( factorial) من النوع ( ::) من عدد صحيح إلى عدد صحيح ( Integer -> Integer). أي أنها تأخذ عددًا صحيحًا كمعامل، وتعيد عددًا صحيحًا آخر. ويُستنتج نوع التعريف تلقائيًا في حال عدم تحديد نوع الدالة.

يعتمد السطر الثاني على مطابقة الأنماط ، وهي ميزة مهمة في لغة هاسكل. لاحظ أن معاملات الدالة لا تُوضع بين قوسين، بل تُفصل بمسافات. عندما تكون قيمة معامل الدالة صفرًا (0)، فإنها تُرجع العدد الصحيح واحدًا (1). في جميع الحالات الأخرى، يُجرَّب السطر الثالث. هذا هو الاستدعاء الذاتي ، الذي يُنفِّذ الدالة مرة أخرى حتى الوصول إلى حالة التوقف.

باستخدام الدالة productمن المقدمة، وعدد من الدوال الصغيرة المشابهة للمكتبة القياسية للغة C ، وباستخدام صيغة Haskell للتسلسلات الحسابية، يمكن التعبير عن دالة المضروب في Haskell على النحو التالي:

المضروب n = حاصل ضرب [ 1 .. n ]

هنا، [1..n]يُمثل التسلسل الحسابي 1، 2، ...، n في شكل قائمة. باستخدام دالة Prelude ، يمكن كتابة enumFromToالتعبير على النحو التالي ، مما يسمح بالتعبير عن دالة المضروب على النحو التالي.[1..n]enumFromTo 1 n

مضروب n = حاصل ضرب ( enumFromTo 1 n )

والتي، باستخدام عامل تركيب الدوال (المعبر عنه كنقطة في لغة هاسكل) لتركيب دالة المنتج مع دالة التعداد الجزئي ، يمكن إعادة كتابتها بأسلوب خالٍ من النقاط : [ 2 ]

المضروب = حاصل الضرب . تعداد من إلى 1

في مُفسِّر Hugs، غالبًا ما يحتاج المستخدم إلى تعريف الدالة واستخدامها في نفس السطر مفصولين بعلامة whereأو let... inعلى سبيل المثال، لاختبار الأمثلة المذكورة أعلاه ورؤية الناتج 120:

ليكن { مضروب العدد n | n > 0 = n * مضروب ( n - 1 مضروب العدد = 1 } مضروب العدد 5

أو

مضروب العدد 5 حيث مضروب العدد = حاصل ضرب العدد . enumFromTo 1

لا يحتوي مترجم GHCi على هذا القيد ويمكن إدخال تعريفات الوظائف في سطر واحد (مع بناء الجملةlet بدون inالجزء)، والإشارة إليها لاحقًا.

أمثلة أكثر تعقيدًا

آلة حاسبة

في كود Haskell الموضح أدناه مباشرةً، ::يمكن قراءة `a` على أنها "له نوع"؛ a -> bويمكن قراءة `b` على أنها "دالة من a إلى b". (وبالتالي، يمكن قراءة كود Haskell على أنه " له نوع دالة من السلاسل النصية إلى قوائم الأعداد العشرية"). في السطر الثاني، يمكن قراءة علامة المساواة على أنها "يمكن أن تكون"؛ وبالتالي، يمكن قراءة الأسطر المتعددة التي تحتوي على `a` على أنها قيم محتملة متعددة لـ `b` ، اعتمادًا على الظروف الموضحة في كل سطر.calc :: String -> [Float]calccalc = ...calc = ...calc

آلة حاسبة بسيطة للتدوين البولندي العكسي، معبر عنها بدالة من الرتبة العليا، حيث يتم تعريف foldlوسيطها f في جملة شرطية باستخدام مطابقة الأنماط وفئة النوع Read :

calc :: String -> [ Float ] calc = foldl f [] . words where f ( x : y : zs ) "+" = ( y + x ) : zs f ( x : y : zs ) "-" = ( y - x ) : zs f ( x : y : zs ) "*" = ( y * x ) : zs f ( x : y : zs ) "/" = ( y / x ) : zs f ( x : y : zs ) "FLIP" = y : x : zs f zs w = read w : zs

القائمة الفارغة هي الحالة الأولية، ويقوم f بتفسير كلمة واحدة في كل مرة، إما كاسم دالة، حيث يأخذ رقمين من رأس القائمة ويدفع النتيجة مرة أخرى، أو يقوم بتحليل الكلمة كرقم فاصلة عائمة وإضافته إلى القائمة.

متتالية فيبوناتشي

ينتج التعريف التالي قائمة بأعداد فيبوناتشي في زمن خطي:

fibs = 0 : 1 : zipWith ( + ) fibs ( tail fibs )

تُنتج القائمة اللانهائية باستخدام الاستدعاء الذاتي المشترك ، حيث تُحسب القيم اللاحقة للقائمة عند الطلب بدءًا من العنصرين الأولين 0 و1. يعتمد هذا النوع من التعريف على التقييم الكسول ، وهي ميزة مهمة في برمجة هاسكل. كمثال على كيفية تطور التقييم، يوضح ما يلي قيم فيبس وفيبس الذيلية بعد حساب ستة عناصر، ويُبين كيف أنتجت الدالة zipWith(+) أربعة عناصر، ثم تنتقل لإنتاج العنصر التالي:

fibs = 0 : 1 : 1 : 2 : 3 : 5 : ...   + + + + + + ذيل فيبوناتشي = 1 : 1 : 2 : 3 : 5 : ...   = = = = = = zipWith ... = 1 : 2 : 3 : 5 : 8 : ... fibs = 0 : 1 : 1 : 2 : 3 : 5 : 8 : ...

نفس الدالة، مكتوبة باستخدام صيغة فهم القوائم المتوازية لمترجم غلاسكو هاسكل (يجب تفعيل ملحقات GHC باستخدام علامة سطر أوامر خاصة، هنا -XParallelListComp ، أو عن طريق بدء ملف المصدر باستخدام ):{-# LANGUAGE ParallelListComp #-}

fibs = 0 : 1 : [ a + b | a <- fibs | b <- tail fibs ]

أو باستخدام تراكيب القوائم العادية :

fibs = 0 : 1 : [ a + b | ( a , b ) <- zip fibs ( tail fibs ) ]

أو الإشارة إلى الذات مباشرة:

fibs = 0 : 1 : التالي fibs حيث التالي ( a : t @ ( b : _ )) = ( a + b ) : التالي t

باستخدام دالة توليد ذات حالة :

fibs = next ( 0 , 1 ) where next ( a , b ) = a : next ( b , a + b )

أو مع unfoldr:

fibs = unfoldr ( \ ( a , b ) -> Just ( a , ( b , a + b ))) ( 0 , 1 )

أو scanl:

fibs = 0 : scanl ( + ) 1 fibs

استخدام التكرار في البيانات مع مُركِّب النقطة الثابتة المُعرَّف مسبقًا في لغة هاسكل :

fibs = fix ( \ xs -> 0 : 1 : zipWith ( + ) xs ( tail xs )) -- zipWith version = fix (( 0 : ) . ( 1 : ) . ( zipWith ( + ) <*> tail )) -- كما هو الحال أعلاه، pointfree = fix (( 0 : ) . scanl ( + ) 1 ) -- scanl version

مضروب

يمكن كتابة المضروب الذي رأيناه سابقًا على شكل سلسلة من الدوال:

مضروب n = foldr (( . ) . ( * )) id [ 1 .. n ] $ 1 -- مضروب 5 == ((1*) .) ((2*) .) ((3*) .) ((4*) .) ((5*) .) id )))) 1 -- == (1*) . (2*) . (3*) . (4*) . (5*) . id $ 1 -- == 1* ( 2* ( 3* ( 4* ( 5* ( id 1 )))))مضروب n = foldr (( . ) . ( * )) ( const 1 ) [ 1 .. n ] $ () -- مضروب 5 == ((1*) .) ((2*) .) ((3*) .) ((4*) .) ((5*) .) (const 1) )))) () -- == (1*) . (2*) . (3*) . (4*) . (5*) . const 1 $ () -- == 1* ( 2* ( 3* ( 4* ( 5* ( const 1 () )))))مضروب n = foldr (( $ ) . ( * )) 1 [ 1 .. n ] = foldr ( $ ) 1 $ map ( * ) [ 1 .. n ] -- مضروب 5 == ((1*) $) ((2*) $) ((3*) $) ((4*) $) ((5*) $) 1 )))) -- == (1*) $ (2*) $ (3*) $ (4*) $ (5*) $ 1 -- == 1* ( 2* ( 3* ( 4* ( 5* 1 ))))

أمثلة أخرى

أرقام هامينغ

دالة موجزة بشكل ملحوظ تُعيد قائمة أرقام هامينغ بالترتيب:

hamming = 1 : map ( 2 * ) hamming ` union ` map ( 3 * ) hamming ` union ` map ( 5 * ) hamming

مثل الحلول المختلفة fibsالمعروضة أعلاه، يستخدم هذا الحل التكرار المشترك لإنتاج قائمة من الأرقام عند الطلب، بدءًا من الحالة الأساسية 1 وبناء عناصر جديدة بناءً على الجزء السابق من القائمة.

unionتُستخدم الدالة هنا كعامل من خلال وضعها بين علامتي اقتباس معكوسة. تحدد بنودها caseكيفية دمج قائمتين تصاعديتين في قائمة تصاعدية واحدة دون تكرار العناصر، مما يمثل المجموعات كقوائم مرتبة. minusوتُنفذ الدالة المرافقة لها عملية فرق المجموعات .

اتحاد ( س : سس ) ( ص : صس ) = مقارنة حالة س ص من LT -> س : اتحاد سس ( ص : صس ) EQ -> س : اتحاد سس ص GT -> ص : اتحاد ( س : سس ) ص اتحاد سس [] = س اتحاد [] ص = ص
ناقص ( س : سس ) ( ص : صس ) = مقارنة حالة س ص من أصغر -> س : ناقص سس ( ص : صس ) يساوي -> ناقص سس ص أكبر -> ناقص ( س : سس ) ص ناقص سس _ = سس --

من الممكن توليد المضاعفات الفريدة فقط، لتحسين كفاءة التشغيل. وبما أنه لا توجد نسخ مكررة، فلا داعي لإزالتها.

smooth235 = 1 : foldr ( \ p s -> fix $ mergeBy ( < ) s . map ( p * ) . ( 1 : )) [] [ 2 , 3 , 5 ] where fix f = x where x = f x -- مُجمِّع النقطة الثابتة، مع المشاركة

يستخدم هذا الدالة الأكثر كفاءة mergeوالتي لا تهتم بالتكرارات (المستخدمة أيضًا في الدالة التالية mergesort):

دمج العناصر الأقل من xs و ys = دمج xs و ys حيث دمج xs [] = xs دمج [] ys = ys دمج ( x : xs ) ( y : ys ) | أقل من y x = y : دمج ( x : xs ) ys | خلاف ذلك = x : دمج xs ( y : ys )

|يبدأ كل شريط عمودي ( ) بشرط شرطي مع تعبير شرطي قبل =العلامة والتعريف المقابل بعد ذلك، والذي يتم تقييمه إذا كان الشرط صحيحًا.

فرز الدمج

إليك خوارزمية فرز الدمج من الأسفل إلى الأعلى ، مُعرّفة باستخدام الدالة ذات الرتبة الأعلىuntil :

mergesortBy less [] = [] mergesortBy less xs = head $ until ( null . tail ) ( pairwise $ mergeBy less ) [[ x ] | x <- xs ]f ( a : b : t ) = f( a, b ) / f( t) = f ( a ,b) / f ( t)

الأعداد الأولية

يمكن ترجمة التعريف الرياضي للأعداد الأولية حرفياً تقريباً إلى لغة هاسكل:

-- "الأعداد الصحيحة الأكبر من 1 التي لا تقبل القسمة على عدد صحيح أصغر منها أكبر من 1" -- الأعداد الأولية = { n ∈ [2..] | ~ ∃ d ∈ [2..n-1] ⇒ rem nd = 0 } -- = { n ∈ [2..] | ∀ d ∈ [2..n-1] ⇒ rem nd ≠ 0 }primes = [ n | n <- [ 2 .. ], all ( \ d -> rem n d /= 0 ) [ 2 .. ( n - 1 )] ]

يجد هذا البرنامج الأعداد الأولية عن طريق القسمة التجريبية . لاحظ أنه غير مُحسَّن من حيث الكفاءة، وأداؤه ضعيف للغاية. أما الكود الذي كتبه ديفيد تيرنر فهو أسرع قليلاً (لكنه لا يزال بطيئًا جدًا) [ 3 ] :

primes = sieve [ 2 .. ] where sieve ( p : xs ) = p : sieve [ x | x <- xs , rem x p /= 0 ]

خوارزمية التقسيم التجريبي الأمثل أسرع بكثير

primes = 2 : [ n | n <- [ 3 .. ], all (( > 0 ) . rem n ) $ takeWhile (( <= n ) . ( ^ 2 )) primes ]

أو غربال غير محدود من إراتوستينس مع تأجيل الغربلة على مراحل، [ 4 ]

primes = 2 : sieve primes [ 3 .. ] where sieve ( p : ps ) ( span ( < p * p ) -> ( h , t )) = h ++ sieve ps ( minus t [ p * p , p * p + p .. ])

أو تطبيق الغربال المدمج بواسطة ريتشارد بيرد ، [ 5 ]

-- "الأعداد الصحيحة الأكبر من 1 بدون أي أعداد مركبة والتي -- يتم إيجادها عن طريق تعداد مضاعفات كل عدد أولي" primes = 2 : minus [ 3 .. ] ( foldr ( \ ( m : ms ) r -> m : union ms r ) [] [[ p * p , p * p + p .. ] | p <- primes ])

أو نسخة طي أسرع تشبه الشجرة [ 6 ] مع تعقيد زمني مثالي تقريبًا (بالنسبة لرمز قائم على القوائم) وتعقيد مكاني منخفض للغاية يتم تحقيقه من خلال الإنتاج المتكرر متعدد المراحل للأعداد الأولية:

primes = 2 : _Y (( 3 : ) . minus [ 5 , 7 .. ] . _U . map ( \ p -> [ p * p , p * p + 2 * p .. ])) where -- مُركِّب Y غير المُشترك: _Y g = g ( _Y g ) -- (g (g (g (g (...))))) -- الاتحاد الكبير ~= nub.sort.concat _U (( x : xs ) : t ) = x : ( union xs . _U . pairwise union ) t

عند العمل على المصفوفات حسب القطاعات بين مربعات الأعداد الأولية المتتالية،

استيراد Data.Array و Data.List ( tails , inits )primes = 2 : [ n | ( r : q : _ , px ) <- zip ( tails ( 2 : [ p * p | p <- primes ])) ( inits primes ), ( n , True ) <- assocs ( accumArray ( \ _ _ -> False ) True ( r + 1 , q - 1 ) [ ( m , () ) | p <- px , s <- [ div ( r + p ) p * p ] , m <- [ s , s + p .. q - 1 ] ] ) ]

ربما يكون أقصر كود ممكن هو ... ولكنه بطيء للغاية. nubBy (((>1) .) . gcd) [2..] 

بناء الجملة

تَخطِيط

تسمح لغة هاسكل باستخدام المسافة البادئة للإشارة إلى بداية تعريف جديد. على سبيل المثال، في جملة شرطية ( where) :

product xs = prod xs 1 where prod [] a = a prod ( x : xs ) a = prod xs ( a * x )

تتم محاذاة المعادلتين الخاصتين بالدالةprod المتداخلة عموديًا، مما يسمح بحذف الفاصلة المنقوطة. في لغة هاسكل، يمكن استخدام المسافة البادئة في العديد من التراكيب النحوية، بما في ذلك doو letو caseو classو instance.

يعود استخدام المسافة البادئة للإشارة إلى بنية البرنامج إلى لغة ISWIM التي ابتكرها بيتر ج. لاندين ، حيث عُرفت باسم قاعدة التجاوز . وقد تبنتها لاحقًا لغة ميراندا ، كما تبنت لغة هاسكل نسخة مشابهة (لكنها أكثر تعقيدًا) من قاعدة التجاوز الخاصة بميراندا، والتي تُسمى "التخطيط". ومن اللغات الأخرى التي اعتمدت بنية حساسة للمسافات البيضاء لغتا بايثون وإف شارب .

يُعدّ استخدام التخطيط في لغة هاسكل اختياريًا. على سبيل المثال، productيمكن كتابة الدالة أعلاه أيضًا بالشكل التالي:

product xs = prod xs 1 where { prod [] a = a ; prod ( x : xs ) a = prod xs ( a * x ) }

يشير القوس المفتوح الصريح بعد whereالكلمة المفتاحية إلى أن التصريحات المنفصلة ستستخدم فواصل منقوطة صريحة، وسيتم إنهاء قائمة التصريحات بقوس مغلق صريح. أحد أسباب الرغبة في دعم المحددات الصريحة هو أنها تُسهّل عملية التوليد التلقائي لشفرة مصدر هاسكل .

تعرضت قاعدة التخطيط في لغة هاسكل لانتقادات بسبب تعقيدها. فعلى وجه الخصوص، ينص تعريفها على أنه إذا واجه المحلل اللغوي خطأً في التحليل أثناء معالجة قسم التخطيط، فعليه محاولة إدراج قوس إغلاق (قاعدة "خطأ التحليل"). يتطلب تطبيق هذه القاعدة في عملية تحليل نحوي وتحليل معجمي تقليدية تعاونًا ثنائي الاتجاه بين المحلل النحوي والمحلل المعجمي، بينما في معظم اللغات، يمكن النظر إلى هاتين المرحلتين بشكل مستقل.

استدعاءات الدوال

يتم التعبير عن تطبيق دالة fعلى قيمة ما ببساطة على النحو التالي :xf x

تُميّز لغة هاسكل بين استدعاءات الدوال ومعاملات التنسيق الوسطي من الناحية التركيبية، ولكن ليس من الناحية الدلالية. يمكن استخدام أسماء الدوال المكونة من علامات ترقيم كمعاملات، وكذلك أسماء الدوال الأخرى إذا كانت محاطة بعلامات اقتباس معكوسة؛ ويمكن استخدام المعاملات في صيغة البادئة إذا كانت محاطة بأقواس.

يوضح هذا المثال الطرق التي يمكن من خلالها استدعاء الدوال:

أضف أ ب = أ + بعشرة1 = 5 + 5 عشرة2 = ( + ) 5 5 عشرة3 = أضف 5 5 عشرة4 = 5 ` أضف ` 5

يمكن تطبيق الدوال التي تُعرَّف بأنها تأخذ عدة معاملات جزئيًا. ويمكن تطبيق المعاملات الثنائية جزئيًا باستخدام ترميز المقاطع .

5 = ( + 5 ) 5 6 = ( 5 + ) 5 أضف خمسة = ( 5 + ) 7 = أضف خمسة 5

فهم القوائم

انظر إلى قسم "فهم القوائم"#نظرة عامة للاطلاع على مثال هاسكل.

مطابقة الأنماط

تُستخدم مطابقة الأنماط لمطابقة مُنشئات أنواع البيانات الجبرية المختلفة. فيما يلي بعض الدوال، حيث تستخدم كل منها مطابقة الأنماط على كل نوع من الأنواع التالية:

يشير هذا التوقيع النوعي إلى أن الدالة `empty` تأخذ قائمة تحتوي على أي نوع، وتعيد قيمة منطقية `Bool`. `empty :: [ a ] ​​-> Bool empty ( x : xs ) = False empty [] = True`-- ستُعيد قيمة من كائن Maybe a، مع قيمة افتراضية في حال مصادفة Nothing. fromMaybe :: a -> Maybe a -> a fromMaybe x ( Just y ) = y fromMaybe x Nothing = xisRight :: Either a b -> Bool isRight ( Right _ ) = True isRight ( Left _ ) = FalsegetName :: Person -> String getName ( Person name _ _ ) = namegetSex :: Person -> Sex getSex ( Person _ sex _ ) = sexgetAge :: Person -> Int getAge ( Person _ _ age ) = age

باستخدام الدوال المذكورة أعلاه، بالإضافة إلى mapالدالة، يمكننا تطبيقها على كل عنصر من عناصر القائمة، لرؤية نتائجها:

map empty [[ 1 , 2 , 3 ], [] ,[ 2 ],[ 1 .. ]] -- تُرجع [False,True,False,False]map ( fromMaybe 0 ) [ Just 2 , Nothing , Just 109238 , Nothing ] -- تُرجع [2,0,109238,0]map isRight [ Left "hello" , Right 6 , Right 23 , Left "world" ] -- returns [False, True, True, False]map getName [ Person "Sarah" Female 20 , Person "Alex" Male 20 , tom ] -- تُرجع ["Sarah", "Alex", "Tom"]، باستخدام تعريف tom أعلاه
  • الأنواع المجردة
  • القوائم

المجموعات

يمكن استخدام المجموعات (tuples) في لغة هاسكل لتخزين عدد ثابت من العناصر. وتُستخدم لتجميع أجزاء من البيانات ذات الأنواع المختلفة.

account :: ( String , Integer , Double ) -- نوع مجموعة ثلاثية، تمثل -- الاسم، والرصيد، ومعدل الفائدة account = ( "John Smith" , 102894 , 5.25 )

تُستخدم المجموعات عادةً في وظائف zip* لوضع العناصر المتجاورة في قوائم منفصلة معًا في مجموعات (يتم توفير zip4 إلى zip7 في وحدة Data.List):

-- تعريف دالة zip. تُعرَّف دوال zip* الأخرى بشكل مشابه: zip :: [ x ] -> [ y ] -> [( x , y )] zip ( x : xs ) ( y : ys ) = ( x , y ) : zip xs ys zip _ _ = []zip [ 1 .. 5 ] "hello" -- تُرجع [(1,'h'),(2,'e'),(3,'l'),(4,'l'),(5,'o')] -- ونوعها [(Integer, Char)]zip3 [ 1 .. 5 ] "hello" [ False , True , False , False , True ] -- تُرجع [(1,'h',False),(2,'e',True),(3,'l',False),(4,'l',False),(5,'o',True)] -- ونوعها [(Integer,Char,Bool)]

في مُترجم GHC، يتم تعريف المجموعات بأحجام تتراوح من عنصرين إلى 62 عنصرًا.

مساحات الأسماء

في قسم "  أمثلة أكثر تعقيدًا" أعلاه، calcيتم استخدام "is" بمعنيين، مما يدل على وجود مساحة اسم لفئة نوع Haskell وأيضًا مساحة اسم للقيم:

  1. فئة نوع هاسكل لـ calc. يمكن تحديد المجال والمدى بشكل صريح في فئة نوع هاسكل.
  2. قيمة أو صيغة أو تعبير في لغة هاسكل لـ calc.

الفئات النوعية وتعدد الأشكال

أنواع البيانات الجبرية

تُستخدم أنواع البيانات الجبرية على نطاق واسع في لغة هاسكل. ومن أمثلة هذه الأنواع: القوائم المدمجة، Maybeوالأنواع Eitherالتالية:

-- قائمة من العناصر a ([a]) هي إما a مدمجة (:) في قائمة أخرى من a، أو قائمة فارغة ([]) data [ a ] ​​= a : [ a ] ​​| [] -- شيء من النوع Maybe a هو إما Just something، أو Nothing data Maybe a = Just a | Nothing -- شيء من النوع Either atype btype هو إما Left atype، أو Right btype data Either a b = Left a | Right b

يمكن لمستخدمي اللغة أيضًا تعريف أنواع البيانات المجردة الخاصة بهم . مثال على نوع بيانات مجرد يُستخدم لتمثيل اسم الشخص وجنسه وعمره قد يبدو كالتالي:

بيانات الجنس = ذكر | أنثى بيانات الشخص = شخص سلسلة نصية الجنس عدد صحيح -- لاحظ أن الشخص هو كل من المُنشئ والنوع-- مثال على إنشاء شيء من نوع Person tom :: Person tom = Person "Tom" Male 27

نظام الكتابة

  • فئات الأنواع
  • تحديد النوع الافتراضي
  • القيم الحرفية الزائدة
  • تعدد الأشكال من النوع الأعلى
  • فئات أنواع متعددة المعلمات
  • التبعيات الوظيفية

المونادات والمدخلات/المخرجات

  • نظرة عامة على إطار عمل الموناد :
  • التطبيقات
    • إدخال/إخراج أحادي
    • تدوين
    • مراجع
    • الاستثناءات

موناد القديس

تتيح موناد ST كتابة خوارزميات البرمجة الإجرائية في لغة هاسكل، باستخدام المتغيرات القابلة للتغيير (STRefs) والمصفوفات القابلة للتغيير (STArrays وSTUArrays). وتكمن ميزة موناد ST في أنها تسمح بكتابة شيفرة ذات تأثيرات جانبية داخلية، مثل التحديث التخريبي للمتغيرات والمصفوفات القابلة للتغيير، مع احتواء هذه التأثيرات داخل الموناد. ونتيجةً لذلك، تبدو الدوال المكتوبة باستخدام موناد ST نقيةً لبقية البرنامج. وهذا يسمح باستخدام الشيفرة الإجرائية حيث قد يكون من غير العملي كتابة شيفرة وظيفية، مع الحفاظ على جميع مزايا الأمان التي توفرها الشيفرة النقية.

إليكم مثالاً لبرنامج (مأخوذ من صفحة ويكي هاسكل حول موناد ST ) يأخذ قائمة من الأرقام، ويجمعها، باستخدام متغير قابل للتغيير:

استيراد Control.Monad.ST استيراد Data.STRef استيراد Control.MonadsumST :: Num a => [ a ] ​​-> a sumST xs = runST $ do -- يقوم runST بتحويل كود ST ذي الحالة إلى كود نقي. summed <- newSTRef 0 -- إنشاء STRef (متغير قابل للتغيير)forM_ xs $ \ x -> do -- لكل عنصر من عناصر قائمة الوسائط xs .. modifySTRef summed ( + x ) -- أضفه إلى ما لدينا في n.readSTRef summed -- اقرأ قيمة n، والتي سيتم إرجاعها بواسطة runST أعلاه.

موناد STM

موناد STM هو تطبيق لذاكرة المعاملات البرمجية في لغة هاسكل. يتم تنفيذه في مُصرّف GHC، ويسمح بتعديل المتغيرات القابلة للتغيير في المعاملات .

الأسهم

  • الدوال التطبيقية
  • الأسهم

بما أن هاسكل لغة وظيفية بحتة، فلا يمكن أن يكون للدوال آثار جانبية. ولأنها غير صارمة، فهي لا تملك ترتيب تقييم محددًا. يُمثل هذا تحديًا للبرامج الحقيقية، التي تحتاج، من بين أمور أخرى، إلى التفاعل مع بيئة معينة. تحل هاسكل هذه المشكلة باستخدام أنواع الموناد التي تستفيد من نظام الأنواع لضمان التسلسل الصحيح للبنى الإجرائية. المثال النموذجي هو الإدخال/الإخراج ، لكن المونادات مفيدة لأغراض أخرى كثيرة، بما في ذلك الحالة القابلة للتغيير، والتزامن، والذاكرة المعاملاتية، ومعالجة الاستثناءات، ونشر الأخطاء.

توفر لغة هاسكل صيغة خاصة للتعبيرات الأحادية، مما يسمح بكتابة البرامج ذات التأثيرات الجانبية بأسلوب مشابه للغات البرمجة الإجرائية الحديثة؛ ولا يتطلب ذلك معرفة بالرياضيات الكامنة وراء عمليات الإدخال والإخراج الأحادية . يقرأ البرنامج التالي اسمًا من سطر الأوامر ويعرض رسالة ترحيب:

main = do putStrLn "ما اسمك؟" name <- getLine putStr ( "مرحباً، " ++ name ++ "! \n " )

تسهّل صيغة "do" التعامل مع المونادات. هذا التعبير "do" مكافئ للنسخة المبسطة التي تستخدم عوامل المونادات مباشرةً ، ولكنه (ربما) أسهل كتابةً وفهمًا منها.

main = putStrLn "ما اسمك؟" >> getLine >>= \ name -> putStr ( "مرحباً، " ++ name ++ "! \n " )
انظر أيضًا wikibooks:Transwiki:List of hello world programs#Haskell للحصول على مثال آخر يقوم بطباعة نص.

التزامن

لا يتضمن تعريف لغة هاسكل التزامن ولا التوازي ، على الرغم من أن GHC يدعم كليهما.

Concurrent Haskell هي امتداد للغة Haskell يدعم الخيوط والتزامن . [ 7 ] يعتمد تطبيق GHC لـ Concurrent Haskell على تجميع خيوط Haskell خفيفة الوزن على عدد قليل من خيوط نظام التشغيل الثقيلة ، [ 8 ] بحيث تعمل برامج Concurrent Haskell بالتوازي عبر المعالجة المتعددة المتناظرة . يمكن لوقت التشغيل دعم ملايين الخيوط المتزامنة. [ 9 ]

تستخدم آلية عمل GHC مجموعة ديناميكية من خيوط نظام التشغيل، مما يسمح لخيط Haskell بإجراء استدعاء نظام حظر دون حظر خيوط Haskell الأخرى قيد التشغيل. [ 10 ] وبالتالي، تتمتع خيوط Haskell الخفيفة بخصائص خيوط نظام التشغيل الثقيلة، وقد لا يكون المبرمج على دراية بتفاصيل التنفيذ.

تم مؤخرًا توسيع لغة Concurrent Haskell لتشمل دعم ذاكرة المعاملات البرمجية (STM)، وهي تجريد للتزامن تُنفذ فيه العمليات المركبة على البيانات المشتركة بشكل ذري، كمعاملات. [ 11 ] يُعد تطبيق STM الخاص بـ GHC التطبيق الوحيد حتى الآن الذي يوفر ضمانًا ثابتًا أثناء الترجمة يمنع تنفيذ العمليات غير المعاملاتية داخل المعاملة. كما توفر مكتبة Haskell STM عمليتين غير موجودتين في تطبيقات STM الأخرى: و ، واللتان تسمحان معًا بتعريف العمليات الحظرية بطريقة معيارية وقابلة للتركيب .retryorElse

مراجع

  1. هاسكل ويكي: توقيعات الأنواع كأسلوب جيد
  2. هاسكل ويكي: بوينت فري
  3. "الأعداد الأولية - هاسكل ويكي" . www.haskell.org .
  4. "الأعداد الأولية - هاسكل ويكي" . www.haskell.org .
  5. أونيل، ميليسا إي، "المنخل الحقيقي لإراتوستينس" ، مجلة البرمجة الوظيفية ، نُشر على الإنترنت بواسطة مطبعة جامعة كامبريدج في 9 أكتوبر 2008 doi : 10.1017/S0956796808007004 ، ص 10، 11.
  6. "الأعداد الأولية - هاسكل ويكي" . www.haskell.org .
  7. سيمون بيتون جونز، وأندرو جوردون، وسيغبيورن فين. هاسكل المتزامنة . ندوة ACM SIGPLAN-SIGACT حول مبادئ لغات البرمجة (PoPL). 1996. (بعض الأقسام قديمة فيما يتعلق بالتنفيذ الحالي.)
  8. دعم وقت التشغيل للغة هاسكل متعددة النوى، مؤرشف في 5 يوليو 2010 على موقع Wayback Machine (سايمون مارلو، سايمون بيتون جونز، ساتنام سينغ) ICFP '09: وقائع المؤتمر الدولي الرابع عشر لجمعية ACM SIGPLAN حول البرمجة الوظيفية، إدنبرة، اسكتلندا، أغسطس 2009
  9. "DEFUN 2009: البرمجة متعددة النوى في Haskell الآن!" . 5 سبتمبر 2009.
  10. توسيع واجهة وظائف Haskell الخارجية بالتزامن، مؤرشف في 2010-07-03 على Wayback Machine (سايمون مارلو، سايمون بيتون جونز، وولفغانغ ثالر)، وقائع ورشة عمل ACM SIGPLAN حول Haskell، الصفحات 57-68، سنو بيرد، يوتا، الولايات المتحدة الأمريكية، سبتمبر 2004
  11. هاريس، تيم؛ مارلو، سيمون ؛ بيتون جونز، سيمون ؛ هيرليهي، موريس (2005). "معاملات الذاكرة القابلة للتركيب". وقائع الندوة العاشرة لجمعية ACM SIGPLAN حول مبادئ وممارسات البرمجة المتوازية . CiteSeerX 10.1.1.67.3686 .