2009-05-01 12 views
67

Ho intenzione di insegnare un corso di divisione inferiore in strutture discrete. Ho selezionato il libro di testo Discrete Structures, Logic, and Computability in parte perché contiene esempi e concetti che favoriscono l'implementazione con un linguaggio di programmazione funzionale. (Penso anche che sia un buon libro.)Haskell o Standard ML per principianti?

Voglio un linguaggio FP di facile comprensione per illustrare i concetti di DS e che gli studenti possono usare. La maggior parte degli studenti avrà avuto solo uno o due semestri di programmazione in Java, nel migliore dei casi. Dopo aver esaminato Scheme, Erlang, Haskell, Ocaml e SML, ho optato per Haskell o Standard ML. Mi sto appoggiando ad Haskell per i motivi illustrati di seguito, ma mi piacerebbe l'opinione di coloro che sono programmatori attivi nell'uno o nell'altro.

  • Sia Haskell che SML hanno un pattern matching che rende la descrizione di un algoritmo ricorsivo un gioco da ragazzi.
  • Haskell ha una buona comprensione delle liste che si adattano bene al modo in cui tali liste sono espresse matematicamente.
  • Haskell ha una valutazione lenta. Ottimo per la costruzione di liste infinite usando la tecnica di comprensione delle liste.
  • SML ha un interprete veramente interattivo in cui le funzioni possono essere definite e utilizzate. In Haskell, le funzioni devono essere definite in un file separato e compilate prima di essere utilizzate nella shell interattiva.
  • SML fornisce una conferma esplicita dell'argomento della funzione e dei tipi restituiti in una sintassi di facile comprensione. Ad esempio: val foo = fn: int * int -> int. La sintassi implicita del curry di Haskell è un po 'più ottusa, ma non totalmente aliena. Ad esempio: foo :: Int -> Int -> Int.
  • Haskell utilizza per impostazione predefinita numeri interi a precisione arbitraria. È una libreria esterna in SML/NJ. E SML/NJ tronca l'output a 70 caratteri per impostazione predefinita.
  • La sintassi lambda di Haskell è sottile: utilizza una sola barra rovesciata. SML è più esplicito. Non sono sicuro se avremo mai bisogno di lambda in questa classe, però.

Essenzialmente, SML e Haskell sono approssimativamente equivalenti. Mi chino verso Haskell perché adoro le liste e le liste infinite in Haskell. Ma sono preoccupato che l'ampio numero di simboli nella sintassi compatta di Haskell possa causare problemi agli studenti. Da quello che ho raccolto leggendo altri post su SO, Haskell non è raccomandato per i principianti che iniziano con FP. Ma non stiamo andando a costruire applicazioni a pieno titolo, semplicemente provando semplici algoritmi.

Cosa ne pensi?


Modifica: dopo aver letto alcune delle tue grandi risposte, dovrei chiarire alcuni dei miei punti elenco.

In SML, non esiste una distinzione sintattica tra la definizione di una funzione nell'interprete e la sua definizione in un file esterno. Diciamo che vuoi scrivere la funzione fattoriale. In Haskell si può mettere questa definizione in un file e caricarlo in GHCi:

fac 0 = 1 
fac n = n * fac (n-1) 

Per me, questo è chiaro, conciso, e corrisponde alla definizione matematica nel libro. Ma se si vuole scrivere la funzione in GHCi direttamente, è necessario utilizzare una sintassi diversa:

let fac 0 = 1; fac n = n * fac (n-1) 

Quando si lavora con gli interpreti interattivi, dal punto di vista didattico è molto, molto utile quando lo studente può utilizzare lo stesso codice sia in un file che nella riga di comando.

Con "conferma esplicita della funzione", intendevo dire che al momento della definizione della funzione, SML indica immediatamente il nome della funzione, i tipi degli argomenti e il tipo di ritorno. In Haskell devi usare il comando :type e poi ottenere la notazione al curry un po 'confusa.

Una cosa più bella di Haskell - si tratta di una definizione di funzione valida:

fac 0 = 1 
fac (n+1) = (n+1) * fac n 

Ancora una volta, questo corrisponde a una definizione che potrebbero trovare nel libro di testo. Non posso farlo in SML!

+3

Informazioni sull'interprete di Haskell, questi sono solo alcuni limiti specifici di GHCi. Forse alcuni altri interpreti Haskell potrebbero avere un comportamento migliore. È possibile definire valori e funzioni in ghci, ma non datatype. Devi iniziare dicendo "lascia". Inoltre, ogni input deve essere su una singola riga, quindi invece della sintassi allineata al rientro su più righe per più definizioni in "let", o per istruzioni in "do" o "case", devi separarli in punti e virgola e circondalo di parentesi. per esempio. "let {fact 0 = 1; fact n = n * fact (n-1)}". Inoltre, le clausole "where" non funzionano. È un po 'noioso – newacct

+0

Informazioni sul currying, SML ha lo stesso supporto per la programmazione e la stessa sintassi di Haskell. Ad esempio, in SML, "fun foo x y = x * y" è una moltiplicazione al curry. È solo che, per qualche ragione, la convenzione in SML è che più argomenti dovrebbero essere dati come un singolo argomento di tupla piuttosto che argomenti al curry. (Si noti che OCaml, tuttavia, segue la convenzione al curry, a differenza di SML.) Tuttavia, questa non è una regola assoluta; le funzioni che sono comunemente parzialmente applicate vengono curried in SML. Ad esempio, la funzione "map" in SML viene elaborata con il tipo "('a ->' b) -> 'a list ->' b list". – newacct

+0

Per impostazione predefinita, Haskell ha sia numeri interi a precisione fissa (Int) che interi a precisione arbitraria (numeri interi). È solo che quando non è vincolato, l'interprete sceglie solo il più ampio possibile, che è Integer. Tuttavia, alcune funzioni accetteranno solo l'Int, ad esempio l'operatore di indicizzazione dell'elenco (!!). – newacct

risposta

81

quanto io amo Haskell, qui ci sono le ragioni preferirei SML per una classe in strutture discrete matematiche e dati (e le classi maggior parte degli altri principianti):

  • tempo e nello spazio costi di I programmi Haskell possono essere molto difficili da prevedere, anche per gli esperti. SML   offre modi molto più limitati per far saltare la macchina.

  • La sintassi per la definizione della funzione in un interprete interattivo è con la sintassi utilizzata in un file, in modo che sia possibile tagliare e incollare.

  • Anche se il sovraccarico dell'operatore in SML è completamente falso, è anche semplice. Sarà difficile insegnare un'intera classe in Haskell senza dover entrare nelle lezioni di tipo.

  • Lo studente può eseguire il debug utilizzando print. (Anche se, come fa notare un commentatore, è possibile ottenere quasi lo stesso effetto in Haskell usando Debug.Trace.trace.)

  • Infinite strutture di dati soffiano le menti delle persone. Per i principianti, è meglio averli definire un tipo di flusso completo di celle ref e thunk, in modo che sappiano come funziona:

    datatype 'a thunk_contents = UNEVALUATED of unit -> 'a 
              | VALUE of 'a 
    type 'a thunk = 'a thunk_contents ref 
    val delay : (unit -> 'a) -> 'a thunk 
    val force : 'a thunk -> 'a 
    

    Ora Non è magia più, e si può andare da qui a flussi (liste infinite).

  • Il layout non è semplice come in Python e può essere fonte di confusione.

Ci sono due posti Haskell ha un vantaggio:

  • Core Haskell è possibile scrivere la firma tipo di una funzione poco prima della sua definizione. Questo è estremamente utile per studenti e altri principianti. Semplicemente non c'è un modo carino per gestire le firme dei tipi in SML.

  • Haskell ha una migliore sintassi concreta. La sintassi di Haskell è un notevole miglioramento rispetto alla sintassi ML. I   hanno scritto un short note about when to use parentheses in an ML program; questo aiuta un po '.

Infine, v'è una spada che taglia in entrambe le direzioni:

  • codice Haskell è puro per default, quindi gli studenti è improbabile che inciampare costrutti impuri (IO monade, monade Stato) per caso . Ma per lo stesso motivo, non possono stampare, e se vuoi fare I/O poi al minimo devi spiegare la notazione do, e return è confusa.

Su un argomento correlato, ecco alcuni consigli per la vostra preparazione corso: non trascurare Purely Functional Data Structures da Chris Okasaki. Anche se non lo fai usare ai tuoi studenti, vorrai sicuramente avere una copia.

+0

Bella risposta! Daremo un'altra occhiata a SML. Intendiamoci, le mie conoscenze sia su Haskell che su SML sono limitate a poche ore ciascuna che lavorano attraverso tutorial online e libri di lettura. Devo ancora scavare nei dettagli più fini. Puoi aiutare con questa domanda? http://stackoverflow.com/questions/740896 –

+12

Concordato su tutto, a parte il debug di "stampa". Puoi usarlo in Haskell. Ci sono sia 'Debug.Trace.trace :: String -> a -> a' che' Debug.Trace.traceShow :: (Mostra a) => a -> b -> b' disponibili che vanno attorno ai limiti IO() ed emette la traccia con entusiasmo. – viraptor

+1

<"La sintassi Haskell è un notevole miglioramento rispetto alla sintassi ML. Ho scritto una breve nota su quando utilizzare le parentesi in un programma ML; questo aiuta un po '">: infatti ... una delle cose che non mi piace con SML, è l'ambiguità che può arrivare con le affermazioni di Case. Sento che l'SML dovrebbe richiedere una parentesi attorno ad esso, per evitare ambiguità quando questi sono annidati. Nel complesso, la quantità non tanto grande di dolori che ho contro SML, sono tutti sintattici. – Hibou57

9

Molte università insegnano Haskell come prima lingua funzionale o addirittura come primo linguaggio di programmazione, quindi non penso che questo sarà un problema.

Avendo svolto alcuni degli insegnamenti in uno di questi corsi, non sono d'accordo sul fatto che le possibili confusioni che identificate siano quelle probabili. Le fonti più probabili di confusione iniziale sono gli errori di analisi causati da un layout errato e messaggi misteriosi sulle classi di tipi quando i valori letterali numerici vengono utilizzati in modo errato.

Inoltre non sono d'accordo con alcun suggerimento che Haskell non sia raccomandato per i principianti che iniziano con FP. È certamente l'approccio del big bang in modi che non sono linguaggi rigorosi con la mutazione, ma penso che sia un approccio molto valido.

+1

Ho visto alcune università alternarsi tra Haskell e ML, a seconda di chi stava insegnando il corso. – hbw

26

Insegniamo Haskell ai primi anni della nostra università. I miei sentimenti a riguardo sono un po 'contrastanti. Da un lato insegnare Haskell ai primi anni significa che non devono disimparare lo stile imperativo. Haskell può anche produrre codice molto conciso che le persone che prima avevano un po 'di Java possono apprezzare.

Alcuni problemi che ho notato gli studenti hanno spesso:

  • pattern matching può essere un po 'difficile, in un primo momento. Gli studenti inizialmente hanno avuto alcuni problemi nel vedere come sono correlati la costruzione del valore e la corrispondenza del modello. Hanno anche avuto alcuni problemi a distinguere tra astrazioni. I nostri esercizi includevano funzioni di scrittura che semplificano l'espressione aritmetica e alcuni studenti hanno difficoltà a vedere la differenza tra la rappresentazione astratta (ad es. Const 1) e la rappresentazione in metanaggio (1).

    Inoltre, se gli studenti sono tenuti a scrivere funzioni lista di elaborazione stessi, fare attenzione sottolineando la differenza tra i modelli

    [] 
    [x] 
    (x:xs) 
    [x:xs] 
    

    A seconda di quanto programmazione funzionale si vuole insegnare loro sulla strada, si potrebbe semplicemente dare loro alcune funzioni di libreria e lasciarli giocare con quello.

  • Non abbiamo insegnato ai nostri studenti sulle funzioni anonime, abbiamo semplicemente detto loro delle clausole where. Per alcuni compiti questo era un po 'prolisso, ma funzionava bene diversamente. Inoltre, non abbiamo parlato di applicazioni parziali; questo è probabilmente abbastanza facile da spiegare in Haskell (a causa della sua forma di scrittura), quindi potrebbe valere la pena mostrarlo a loro.

  • scoprirono rapidamente list comprehension e li preferivano over funzioni di ordine superiore come filter, map, zipWith.

  • Penso che ci siamo persi un po 'insegnando loro come lasciarli guidare i loro pensieri dai tipi.Non sono abbastanza sicuro, tuttavia, se questo sia utile ai principianti o meno.

  • I messaggi di errore di solito non sono molto utili per i principianti, a volte potrebbero aver bisogno di aiuto con questi. Non l'ho provato da solo, ma c'è un compilatore Haskell specificamente mirato ai nuovi arrivati, principalmente tramite migliori messaggi di errore: Helium

  • Per i programmi di piccole dimensioni, le cose come possibili perdite di spazio non erano un problema.

Nel complesso, Haskell è un buon linguaggio di insegnamento, ma ci sono alcune insidie. Dato che gli studenti si sentono molto più a loro agio con la comprensione delle liste rispetto alle funzioni di ordine superiore, questo potrebbe essere l'argomento di cui hai bisogno. Non so quanto è lungo il tuo corso o quanta programmazione vuoi insegnare, ma pianifica un po 'di tempo per insegnare loro i concetti di base: ne avranno bisogno.

+3

Qual è la differenza tra i modelli (x: xs) e [x: xs]? – Kiwi

+10

'(x: xs)' corrisponde a una lista non vuota, vale a dire, l'argomento deve avere tipo '[a]'. '[x: xs]' corrisponde ad una lista singleton che contiene una lista non vuota, cioè l'argomento deve avere tipo '[[a]]'. '[x: xs]' è uguale a '[(x: xs)]' che è lo stesso di '((x: xs): [])'. – nominolo

+0

Ri: "Da un lato insegnare Haskell ai primi anni significa che non devono disimparare lo stile imperativo": non lo darei per scontato. Molti studenti avranno già qualche background di programmazione da classi di scuole superiori, calcolatrici grafiche, ecc. – ruakh

11

BTW,

# SML ha una veramente interattiva interprete in cui funzioni possono essere sia definiti e utilizzati. In Haskell, le funzioni devono essere definite in un file separato e compilate prima dell'uso dello nella shell interattiva.

È impreciso. Uso GHCi:

Prelude> let f x = x^2 
Prelude> f 7 
49 
Prelude> f 2 
4 

Ci sono anche buone risorse per Haskell in materia di istruzione sul haskell.org edu. pagina, con esperienze di diversi insegnanti. http://haskell.org/haskellwiki/Haskell_in_education

Infine, sarete in grado di insegnare loro il parallelismo multicore solo per divertimento, se si utilizza Haskell :-)

+5

Ho chiarito la mia dichiarazione sull'interprete SML vs Haskell. Norman Ramsey lo ha detto meglio: copia e incolla dall'interprete in un file. –

+3

Si noti che SML non è solo SML/NJ, che è single-core. Poly/ML supporta i thread nativi e le relative astrazioni utili di Isabelle/ML come elenchi e future paralleli. – Makarius

6
  • SML ha un interprete veramente interattiva in cui le funzioni possono essere sia definiti e Usato. In Haskell, le funzioni devono essere definite in un file separato e compilate prima di essere utilizzate nella shell interattiva.

Mentre Abbracci possono avere questa limitazione, GHCi non lo fa:

$ ghci 
GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer ... linking ... done. 
Loading package base ... linking ... done. 
Prelude> let hello name = "Hello, " ++ name 
Prelude> hello "Barry" 
"Hello, Barry" 

Ci sono molti motivi che preferisco GHC (i) sopra Abbracci, questo è solo uno di loro.

  • SML dà conferma esplicita dell'argomento funzioni e tipi restituiti in una sintassi che è facile da capire. Ad esempio: val foo = fn: int * int -> int. La sintassi implicita del curry di Haskell è un po 'più ottusa, ma non totalmente aliena. Ad esempio: foo :: Int -> Int -> Int.

SML ha quello che si chiama sintassi "implicita curry" pure.

$ sml 
Standard ML of New Jersey v110.69 [built: Fri Mar 13 16:02:47 2009] 
- fun add x y = x + y; 
val add = fn : int -> int -> int 

Essenzialmente, SML e Haskell sono approssimativamente equivalenti. Mi chino verso Haskell perché adoro le liste e le liste infinite in Haskell.Ma sono preoccupato che l'ampio numero di simboli nella sintassi compatta di Haskell possa causare problemi agli studenti. Da quello che ho raccolto leggendo altri post su SO, Haskell non è raccomandato per i principianti che iniziano con FP. Ma non stiamo andando a costruire applicazioni a pieno titolo, semplicemente provando semplici algoritmi.

Mi piace usare Haskell molto più di SML, ma vorrei ancora insegnare SML prima.

  • Assecondare i pensieri di nominolo, list comprehension fanno sembrano rallentare gli studenti di raggiungere alcune funzioni di ordine superiore.
  • Se si desidera la pigrizia e le liste infinite, è istruttivo implementarlo esplicitamente.
  • Poiché SML è valutato con entusiasmo, il modello di esecuzione è molto più facile da comprendere e "il debugging tramite printf" funziona molto meglio che in Haskell.
  • Il sistema di tipo SML è anche più semplice. Mentre la tua classe probabilmente non li userebbe comunque, i typeclass di Haskell sono ancora una sorpresa per finire - far capire loro la distinzione 'a rispetto a ''a in SML è abbastanza difficile.
3

Sono stupito che non stiate considerando OCaml e F # dato che si occupano di molte delle vostre preoccupazioni. Sicuramente gli ambienti di sviluppo decenti e utili sono una priorità per gli studenti? SML è molto indietro e F # è molto più avanti di tutti gli altri FPL in questo senso.

Inoltre, sia OCaml che F # hanno una lista di comprensione.

+1

Ho visto entrambi. Una cosa, in particolare, mi dà fastidio su OCaml: la sintassi per definire una funzione ricorsiva e non ricorsiva è leggermente diversa. Ci sembra banale, ma può essere una grande cosa per un principiante che ha abbastanza problemi con la sintassi. Sto cercando strumenti che mi consentano di entrare nel vivo del problema il più rapidamente possibile. Non ho preso seriamente in considerazione F # poiché si tratta di una lingua solo Microsoft. –

+0

In OCaml, si utilizza "let" per il valore non ricorsivo o le definizioni di funzione e "let rec" per le definizioni ricorsive. In SML, di solito si usa "val" per i valori non funzionali e "fun" per le funzioni tranne quando si tratta di sostituzioni non ricorsive delle funzioni precedenti, nel qual caso è necessario utilizzare "val" e una funzione lambda al livello superiore o "let val" e una funzione lambda se è nidificata. Inutile dire che la via OCaml mi sembra molto più chiara. Inoltre, non hai cose come i tipi di uguaglianza da spiegare. –

+0

Sebbene F # sia solo Microsoft, è possibile scrivere nell'intersezione di OCaml e F # mentre si beneficia ancora dell'ambiente di sviluppo di F #. Ciò renderebbe sicuramente la vita molto più facile per un principiante che deve combattere con gli strumenti comparativamente terribili disponibili per SML e Haskell. –

5

La maggior parte delle risposte erano tecniche, ma penso che dovresti considerarne almeno una che non lo è: Haskell (come OCaml), in questo momento, ha una comunità più grande che lo utilizza in una gamma più ampia di contesti. C'è anche un grande database di librerie e applicazioni scritte per profitto e divertimento allo Hackage. Questo potrebbe essere un fattore importante per mantenere alcuni dei tuoi studenti che usano la lingua al termine del corso e magari provare altri linguaggi funzionali (come Standard ML) in seguito.

0

Haskell. Sono avanti nella mia classe algos/theory in CS a causa delle cose che ho imparato usando Haskell. È un linguaggio così completo e ti insegnerà una tonnellata di CS, semplicemente usando.

Tuttavia, SML è molto più facile da apprendere. Haskell ha caratteristiche come la valutazione pigra e le strutture di controllo che lo rendono molto più potente, ma con il costo di una curva di apprendimento ripida (ish). SML non ha tale curva.

Detto questo, la maggior parte di Haskell era disimparare roba da linguaggi meno scientifici/matematici come Ruby, ObjC o Python.