2010-02-12 3 views
19

que je veux savoir si F # a une fonction de bibliothèque de typeLa documentation F # permet-elle de rechercher des fonctions par type?

('T -> bool) -> 'T list -> int 

-à-dire quelque chose qui compte le nombre d'éléments d'une liste que la fonction retourne vrai pour. (ou renvoie l'index du premier élément qui renvoie true)

J'avais l'habitude d'utiliser la grande liste sur le site MSR pour F # avant que la documentation sur MSDN soit prête. Je pourrais juste rechercher la page pour le texte ci-dessus parce que les types ont été énumérés. Mais maintenant la documentation MSDN liste uniquement les types sur les pages individuelles - la page du module est une bouillie de texte descriptif. Google fonctionne un peu sorta-, mais il ne peut pas aider à

// compatible interfaces 
('T -> bool) -> Seq<'T> -> int 
// argument-swaps 
Seq<'T> -> ('T -> bool) -> int 
// type-variable names 
('a -> bool) -> Seq<'a> -> int 
// wrappers 
('a -> bool) -> 'a list -> option<int> 
// uncurried versions 
('T -> bool) * 'T list -> int 
// .NET generic syntax 
('T -> bool) -> List<'T> -> int 
// methods 
List<'T> member : ('T -> bool) -> int 

Haskell a un programme autonome pour cette appelé Hoogle. Est-ce que F # a un équivalent, comme Fing ou quelque chose?

Répondre

10

Basé sur la réponse de kvb, j'ai créé une application complète. Il est hébergé sur github au http://github.com/sandersn/fing.

Le code est encore assez moche, mais cela fonctionne pour les cas simples. J'ai sorti le plus général-unificateur de kvb (mgu) pour le moment car il ajoute beaucoup de résultats non-évidents. Les choses fantaisistes comme les contraintes structurelles et le super-type général ne fonctionnent pas non plus.

Il existe également un binaire pour une version de ligne de commande si vous ne souhaitez pas générer à partir de la source. (Il faut quand même qu'une version moderne de l'environnement d'exécution .NET soit installée.) Finalement, je trouverai de l'hébergement ASP.NET, j'apprendrai l'ASP et j'emballerai le tout dans une application web pour qu'aucune installation ne soit nécessaire. (Je suppose que s'il y a une demande, je pourrais créer une interface utilisateur côté client, mais j'ai encore moins d'expérience avec ce genre de chose.)

17

Je ne connais aucun de ces outils. Cependant, il pourrait être amusant d'en écrire un en utilisant System.Reflection (ou mieux, la bibliothèque de métadonnées dans le PowerPack), de sorte que vous puissiez prendre en compte les noms de variable de type modulo d'équivalence, etc.

EDIT - J'avais raison - était un exercice amusant. Ce qui suit a beaucoup de verrues, mais n'est pas trop mauvais pour ~ 150 lignes de code. Espérons que cela suffira pour lancer quelqu'un qui veut travailler sur un vrai outil. Il ne fait rien d'avancé comme la vérification des fonctions avec des paramètres réordonnés, et la bibliothèque de métadonnées est un peu difficile à utiliser en utilisant des noms complets, vous devez donc faire attention. Pour répondre à la question dans votre message original, j'exécuté

find "('a -> Microsoft.FSharp.Core.bool) -> Microsoft.FSharp.Collections.list`1<'a> -> Microsoft.FSharp.Core.int" 

et a obtenu la liste suivante des candidats:

Microsoft.FSharp.Core.Operators.(+) 
Microsoft.FSharp.Core.Operators.(-) 
Microsoft.FSharp.Core.Operators.(*) 
Microsoft.FSharp.Core.Operators.(/) 
Microsoft.FSharp.Core.Operators.(%) 
Microsoft.FSharp.Core.Operators.sqrt 
Microsoft.FSharp.Core.LanguagePrimitives.EnumOfValue 
Microsoft.FSharp.Core.LanguagePrimitives.EnumToValue 
Microsoft.FSharp.Core.LanguagePrimitives.AdditionDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.CheckedAdditionDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.MultiplyDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.CheckedMultiplyDynamic 
Microsoft.FSharp.Core.LanguagePrimitives.GenericZero 
Microsoft.FSharp.Core.LanguagePrimitives.GenericOne 
Microsoft.FSharp.Collections.List.find 
Microsoft.FSharp.Collections.List.findIndex 
Microsoft.FSharp.Collections.List.maxBy 
Microsoft.FSharp.Collections.List.minBy 

Parmi ceux-ci, seulement List.findIndex a exactement le type générique que vous cherchez, mais avec la bonne combinaison de paramètres de type faire ainsi les autres (par exemple si 'a = int puis List.find a le type désiré). Malheureusement, les contraintes ne sont pas prises en compte dans la recherche, de sorte que les fonctions non-List ne peuvent pas correspondre. Sans plus attendre, voici le code que j'ai utilisé - vous devrez ajouter une référence à l'assembly FSharp.PowerPack.Metadata pour que cela fonctionne.

open Microsoft.FSharp.Metadata 
open System.Text.RegularExpressions 

(* type parameters let us switch out representation if need be *) 
type Tag<'ty> = | Tuple | Arr | Ground of 'ty 
type Ty<'ty,'a> = Param of 'a | Complex of Tag<'ty> * Ty<'ty,'a> list 

(* Gets something stable from an FSharpEntity so that we can see if two are identical *) 
let rec getType (e:FSharpEntity) = 
    if (e.IsAbbreviation) then 
    getType e.AbbreviatedType.NamedEntity 
    else 
    e.ReflectionType 

(* FSharpType -> Ty<System.Type,string> *) 
let rec cvt (e:FSharpType) = 
    if e.IsTuple then 
    Complex(Tuple, e.GenericArguments |> Seq.map cvt |> List.ofSeq) 
    elif e.IsFunction then 
    Complex(Arr, e.GenericArguments |> Seq.map cvt |> List.ofSeq) 
    elif e.IsGenericParameter then 
    Param e.GenericParameter.Name 
    else 
    Complex(Ground(e.NamedEntity |> getType), e.GenericArguments |> Seq.map cvt |> List.ofSeq) 

(* substitute type for variable within another type *) 
let rec subst v t = function 
| Complex(tag,l) -> Complex(tag, l |> List.map (subst v t)) 
| Param i when i = v -> t 
| Param j -> Param j 

(* get type variables used in a type *) 
let rec usedVars = function 
| Param i -> Set.singleton i 
| Complex(tag, l) -> Set.unionMany (List.map usedVars l) 

(* Find most general unifier (if any) for two types *) 
let mgu t1 t2 = 
    let rec mgu subs = function 
    | [] -> Some subs 
    | (Complex(tag1,l1),Complex(tag2,l2))::rest -> 
     if tag1 <> tag2 then 
     None 
     else 
     let rec loop r = function 
     | [],[] -> mgu subs r 
     | [],_ | _,[] -> None 
     | x::xs, y::ys -> loop ((x,y)::r) (xs,ys) 
     loop rest (l1,l2) 
    | (Param i, Param j)::rest when i = j -> mgu subs rest 
    | ((Param i, x) | (x, Param i))::rest -> 
     if (Set.contains i (usedVars x)) then 
     None (* type would be infinite when unifying *) 
     else 
     mgu ((i,x)::subs) (rest |> List.map (fun (t1,t2) -> (subst i x t1, subst i x t2))) 
    mgu [] [t1,t2] 

(* Active patterns for parsing - this is ugly... *) 
let (|StartsWith|_|) r s = 
    let m = Regex.Match(s, r) 
    if m.Success && m.Index = 0 then 
    Some(m.Value, s.Substring(m.Length)) 
    else None 

let rec (|Any|) (|P|_|) = function 
| P(x,Any (|P|_|) (l,r)) -> x::l, r 
| s -> [],s 

let rec (|Any1|_|) (|P|_|) = function 
| P(x,Any (|P|_|) (l,r)) -> Some(x::l, r) 
| _ -> None 

let (|Seq|_|) (|P|_|) (|Q|_|) = function 
| P(x,Q(y,r)) -> Some((x,y),r) 
| _ -> None 

let (|Choice|_|) (|P|_|) (|Q|_|) = function 
| P(p) -> Some p 
| Q(p) -> Some p 
| _ -> None 

let (|Delimit|_|) s (|P|_|) = function 
| P(x,Any ((|Seq|_|) ((|StartsWith|_|) s) (|P|_|)) (l,r)) -> Some(x::(List.map snd l), r) 
| _ -> None 

let (|Delimit1|_|) s (|P|_|) = function 
| P(x,StartsWith s (_,Delimit s (|P|_|) (l,r))) -> Some(x::l, r) 
| _ -> None 

(* Basically a BNF grammar for types *) 
let rec (|TyE|_|) = function 
| ArrE(p) | TupleE(p) | AtomE(p) -> Some(p) 
| _ -> None 
and (|ArrE|_|) = function 
| Choice (|TupleE|_|) (|AtomE|_|) (dom,StartsWith "->" (_,TyE(rng,r))) -> Some(Complex(Arr,[dom;rng]), r) 
| _ -> None 
and (|TupleE|_|) = function 
| Delimit1 @"\*" (|AtomE|_|) (l,r) -> Some(Complex(Tuple,l), r) 
| _ -> None 
and (|AtomE|_|) = function 
| ParamE(x,r) | GroundE(x,r) | StartsWith @"\(" (_,TyE(x,StartsWith @"\)" (_,r))) -> Some(x,r) 
| _ -> None 
and (|ParamE|_|) = function 
| StartsWith "'[a-zA-Z0-9]+" (s,r) -> Some(Param s, r) 
| _ -> None 
and (|GroundE|_|) = function 
| StartsWith "[`.a-zA-Z0-9]+" (gnd, StartsWith "<" (_, Delimit "," (|TyE|_|) (l, StartsWith ">" (_,r)))) -> 
     let ty = FSharpAssembly.FSharpLibrary.GetEntity gnd |> getType 
     Some(Complex(Ground(ty), l), r) 
| StartsWith "[`.a-zA-Z0-9]+" (gnd, r) -> 
     let ty = FSharpAssembly.FSharpLibrary.GetEntity gnd |> getType 
     Some(Complex(Ground(ty), []), r) 
| _ -> None 

(* parse a string into a type *) 
let parse (s:string) = 
    (* remove whitespace before matching *) 
    match s.Replace(" ","") with 
    | TyE(ty,"") -> ty 
    | _ -> failwith "Not a well-formed type" 

(* an infinite stream of possible variable names - for performing renaming *) 
let rec names = 
    let letters = ['a' .. 'z'] |> List.map string 
    seq { 
    yield! letters 
    for n in names do 
     for l in letters do 
     yield n + l 
    } 

(* finds entities in the F# library with the requested signature, modulo type parameter unification *) 
let find s = 
    let ty = parse s 
    let vars = usedVars ty 
    seq { 
    for e in FSharpAssembly.FSharpLibrary.Entities do 
    for m in e.MembersOrValues do 
     (* need try/catch to avoid error on weird types like "[]`1" *) 
     match (try Some(cvt m.Type) with _ -> None) with 
     | Some ty2 -> 
     (* rename all type variables from the query to avoid incorrectly unifying with type variables in signatures *) 
     let used = usedVars ty2 
     let newVars = Seq.choose (fun v -> if Set.contains v used then None else Some(Param v)) names 
     let varMap = Map.ofSeq (Seq.zip vars newVars) 
     let ty = Map.fold (fun t v p -> subst v p t) ty varMap 
     match mgu ty ty2 with 
     | None ->() 
     | Some _ -> yield sprintf "%s.%s.%s" e.Namespace e.DisplayName m.DisplayName 
     | _ ->() } 
+0

de Grâce, cela ressemble beaucoup. Quand j'aurai le temps, j'essaierai de travailler sur quelque chose d'aussi utilisable que Hoogle. –

5

Ceci est le dernier et le plus grand: http://fsdn.azurewebsites.net/

De la documentation: https://github.com/fsdn-projects/FSDN

signatures API supportées

API signature      Query example 
Functions and values in modules int -> string 
Fields of records and structs  Ref<'a> => 'a 
Methods and properties   'a list -> int or 'a list => int 
Constructors      string -> Uri 
Names (function and method names) head : 'a list -> 'a 
Active patterns     (||) : ... -> Expr -> ? 
Questions connexes