10
votes

Exemples de programmes fonctionnels 'S'écriture eux-mêmes' via une analyse de type

(Contexte: J'ai réfléchi à faire une présentation sur la programmation F # et fonctionnelle. De l'expérience, je pense que le facteur «WOW» de la correspondance et de la déduction du type n'est pas nécessairement suffisant pour contrer l'aide » ! «Facteur de» Où sont mes supports et des points-virgules frisés, mon code va tomber du bord! ». Ce qui m'a fait penser au véritable facteur wow - pour moi - qui est 1) que s'il compile, cela signifie généralement que cela signifie que que cela fonctionne et 2) que vous pouvez souvent déduire la mise en œuvre des types)

Il y a un vidéo sur Channel9 avec Brian Beckman et Erik Meijer où ils ont mentionné comment la mise en œuvre est parfois" tombe "de la signature de type d'une fonction. J'ai également vécu cela dans le passé, mais je ne peux pas proposer un bon exemple qui serait suffisamment simple à présenter à une personne sans expérience fonctionnelle antérieure.

Quelqu'un a-t-il un bon exemple à partager? (il n'est pas nécessaire d'être en f #)

mise à jour

Si c'est une aide, je pense que nous devons y penser différemment: le casse-tête est le suivant:

J'ai des données avec un type donné, je veux le transformer en un type différent et J'ai un ensemble de fonctions avec des signatures données.

Ceci est le "LEGO" que vous devez brancher.


2 commentaires

Je n'ai pas d'exemple simple de cela (mais je continuerai à la nouilles dessus ...) Je peux penser à un certain nombre d'exemples compliqués ... Hmmm ...


Brian: Je serais intéressé à voir même un exemple compliqué! Il pourrait également servir de point de départ pour la communauté SO / F # pour distiller un exemple plus simple si votre «noodling» vient à rien ...


9 Réponses :


3
votes

bas

OK, ce n'est pas un excellent exemple, mais "OK", et peut-être aidera à obtenir d'autres idées ... p>

Supposons P>

let f() = Unchecked.defaultof<'a>


1 commentaires

En fait, dans Haskell, vous pouvez jeter des exceptions à partir du code purs, mais ne les attrapez que dans le io monad. Donc f () = erreur "kaboom!" aurait le type () -> A . (Voir "une sémantique pour des exceptions imprécises" pour les détails sous-jacents de la raison pour laquelle cela va bien.)



4
votes

Identity

Supposons que je souhaite écrire une fonction f xxx

dans F #, je pense que la seule implémentation intéressante est la suivante: xxx < / Pre>

La fonction d'identité ci-dessus est la mise en œuvre naturelle de la plupart des langues.

et comme dans mon autre réponse, vous pouvez également boucler, lancer ou par défaut à initialiser. Mais ce sont moins intéressants. Ces "backdoors" jettent un peu une clé dans les œuvres de tous ces calculs "de type dérivés", il est donc préférable de les ignorer.

La clé de celui-ci est, pour tous les types "A, vous Ne rien savoir sur le type, le seul moyen d'obtenir un véritable objet de ce type est que quelqu'un vous en donne déjà un. La fonction d'identité est donc la seule mise en œuvre raisonnable de cette signature.


0 commentaires

5
votes

tuyau

Voici un troisième exemple ...

Supposons que je veux écrire une fonction xxx

c'est-à-dire que je prends des arguments une valeur de type 'A et une fonction qui prend un "A et retourne a" b. Et mon résultat devrait être un 'b. Eh bien, encore une fois, des boucles et des exceptions infinies modulo et d'initialisation par défaut, il n'y a qu'une seule implication: xxx

"P" peut ne pas ressembler trop utile tant que vous ne réalisiez que c'est l'opérateur de pipeline ( |>).

hm, je me sens comme ces exemples jusqu'à présent sont insuffisants.


0 commentaires

6
votes

Commencez par la fonction la plus simple possible: Identity :: 'A ->' A code>. Combien d'implémentations pouvez-vous penser? Si vous me donnez un A code>, il n'y a qu'une chose que je puisse faire avec cela pour vous donner un A code>. Je vous rends le même A code> que vous m'avez donné, alors:

let compose f g x = f (g x)


3 commentaires

Peut-être que je manque quelque chose (s'il vous plaît éclairer moi), mais ne serait-il pas carré avoir la même signature que l'identité? Laissez SQ X = x * x


@Markus, c'est ce que je pensais aussi, mais (je pense) alors ce ne serait pas "A ->" A, mais INT -> int (ou quelque chose d'autre qui supporte * l'opérateur)


L'un des avantages des signatures de type est qu'ils vous disent tout autant de ce que vous ne pouvez pas faire avec une valeur comme ce que vous pouvez. Vous ne pouvez pas sécuriser un 'A (ou faire une grande partie de rien) car la fonction n'est pas définie pour tous les types. Ces limites font partie de ce qui le rend si facile de traduire une signature de type en code - il existe un nombre limité de choses que vous pouvez faire avec des variables de type générique et une liste encore plus limitée de choses qui ont du sens à faire.



5
votes

Carte

Un couple de plus à envisager ... p> xxx pré>

La seule implémentation intéressante est l'option.map:

('a -> 'b) -> 'a list -> 'b list

Maintenant, j'aurais pu écrire P> xxx pré>

et ignorer simplement les arguments et revenir toujours aucun. Mais ce n'est pas intéressant. Quelqu'un nous passe tous ces arguments utiles, nous sommes sûrement censés faire quelque chose avec eux, non? La première mise en œuvre ci-dessus est donc la seule (à nouveau modulo les trivialités mentionnées dans d'autres réponses, en raison de la boucle, des effets, etc.). P>

De même p>

let lm f xs =
    match xs with
    | [] -> []
    | h::t -> [f h]


1 commentaires

Le type réel de let om f xo = Aucun est 'A ->' B -> 'C Option , il ne s'agit pas seulement d'uninterest, mais du mauvais type. Type Annotations Nonobstant.



2
votes
(a -> b) -> [a] -> [b]
The type practically is the implementation.

0 commentaires

2
votes

Exemple de démarreur idiot, suivant de ma mise à jour:

Donnez que j'ai une liste code>, comment puis-je accéder à tableau code>, Avec les fonctions actuelles (avec une jetée pour Obfuscation!) P>

//Start at the beginning
let input:List<string> = myData
// the only thing that I can apply to this is a 
//     function that starts with List<something>, so...

input |> fn2 // List<'a> -> Array<'b>, so now I have Array<string>
// At this point, it looks like I have no choice but fn3, but that takes 
//   me back where I came from. However, there is an Array<'a> in 
//   the middle of fn4.

input |> fn2 |> fn4 ???
//oops, I need something else here, a function that goes ('a -> 'b).
//   But all my functions go ('a -> 'b)! However, in this case my 'a is a string,
//   so that limits my choice to fn1:

input |> fn2 |> fn4 fn1 // so here I have Array<float> yoohoo!

//recapitulate with the real function names
let convert input = 
    input |> Array.ofList    //List<string> -> Array<string>
          |> Array.map float //Array<string> -> (string -> float) -> Array<float>


0 commentaires

0
votes

Une autre réponse provient de Wes Dyer , qui mentionne ce phénomène précis lors de l'explication de Select pour iobservable. La signature étant:

IObservable<U> Select<T, U>(this IObservable<T> source, Func<T, U> selector)


0 commentaires

3
votes

voir

meilleure façon de condenser une liste d'options de type à seulement des éléments qui ne sont pas non plus? P>

Brièvement, l'objectif est de résoudre pour f CODE> ICI: P>

> [Some 4; None; Some 2; None] |> f;; 
val it : int list = [4; 2] 


0 commentaires