Is a memoized pure function itself considered pure?Haskell ways to the 3n+1 problemCompute if a function is...

What is this fighter jet at Weymouth NAS?

Prevent use of CNAME record for untrusted domain

Is first Ubuntu user root?

Did Dr. Hannibal Lecter like Clarice or was he attracted to her?

How can I download a file from a host I can only SSH to through another host?

Open subspaces of CW complexes

Why did my folder names end up like this, and how can I fix this using a script?

How many birds in the bush?

Redacting URLs as an email-phishing preventative?

Why is the UK so keen to remove the "backstop" when their leadership seems to think that no border will be needed in Northern Ireland?

How to use properly "sich selbst"

Breaker Mapping Questions

Location of label edges in Tikz Graph

"There were either twelve sexes or none."

Beginner to guitar playing - where should I begin?

Billiard balls collision

Joining lists with same elements

Is this password scheme legit?

Number of academics in various EU countries

Do you pay one or two mana to bounce a transformed Delver of Secrets with Repeal?

Hangman game in Python - need feedback on the quality of code

Contours of a national emergency in the United States

What's special ammo in Destiny 2?

Where does learning new skills fit into Agile?



Is a memoized pure function itself considered pure?


Haskell ways to the 3n+1 problemCompute if a function is pureIs this method pure?When to use [Pure] on a constructor?Programatically determine that some functions are pureCan a function be pure if it depends on an immutable instance field?Is anything gained by making dependencies explicit via function argument lists when implementing pure methods?Naming conventions for pure functionsPure functional vs tell, don't ask?






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







8















Let’s say fn(x) is a pure function that does something expensive, like returning a list of the prime factors of x.



And let’s say we make a memoized version of the same function called memoizedFn(x). It always returns the same result for a given input, but it maintains a private cache of previous results to improve performance.



Formally speaking, is memoizedFn(x) considered pure?



Or is there some other name or qualifying term used to refer to such a function in FP discussions? (i.e. a function with side effects that may affect the computational complexity of subsequent calls but that may not affect the return values.)










share|improve this question






















  • 5





    Maybe it is not pure for purists, but "pure enough" for pragmatic people ;-)

    – Doc Brown
    10 hours ago













  • @DocBrown I agree, just wondering if there’s a more formal term for ‘pure enough’

    – callum
    10 hours ago













  • Executing a pure function will most likely modify the processor's instruction cache, branch predictors etc. But that's probably "pure enough" for purists as well - or you can forget about pure functions altogether.

    – gnasher729
    8 hours ago











  • @callum No, there is no formal definition of "pure enough". When arguing about purity and the semantic equivalence of two "referentially transparent" calls, you always have to state exactly which semantics you are going to apply. At some low level of implementation detail it will always break down and have different memory effects or timings. That's why you have to be pragmatic: what level of detail is useful for reasoning about your code?

    – Bergi
    52 mins ago




















8















Let’s say fn(x) is a pure function that does something expensive, like returning a list of the prime factors of x.



And let’s say we make a memoized version of the same function called memoizedFn(x). It always returns the same result for a given input, but it maintains a private cache of previous results to improve performance.



Formally speaking, is memoizedFn(x) considered pure?



Or is there some other name or qualifying term used to refer to such a function in FP discussions? (i.e. a function with side effects that may affect the computational complexity of subsequent calls but that may not affect the return values.)










share|improve this question






















  • 5





    Maybe it is not pure for purists, but "pure enough" for pragmatic people ;-)

    – Doc Brown
    10 hours ago













  • @DocBrown I agree, just wondering if there’s a more formal term for ‘pure enough’

    – callum
    10 hours ago













  • Executing a pure function will most likely modify the processor's instruction cache, branch predictors etc. But that's probably "pure enough" for purists as well - or you can forget about pure functions altogether.

    – gnasher729
    8 hours ago











  • @callum No, there is no formal definition of "pure enough". When arguing about purity and the semantic equivalence of two "referentially transparent" calls, you always have to state exactly which semantics you are going to apply. At some low level of implementation detail it will always break down and have different memory effects or timings. That's why you have to be pragmatic: what level of detail is useful for reasoning about your code?

    – Bergi
    52 mins ago
















8












8








8


1






Let’s say fn(x) is a pure function that does something expensive, like returning a list of the prime factors of x.



And let’s say we make a memoized version of the same function called memoizedFn(x). It always returns the same result for a given input, but it maintains a private cache of previous results to improve performance.



Formally speaking, is memoizedFn(x) considered pure?



Or is there some other name or qualifying term used to refer to such a function in FP discussions? (i.e. a function with side effects that may affect the computational complexity of subsequent calls but that may not affect the return values.)










share|improve this question
















Let’s say fn(x) is a pure function that does something expensive, like returning a list of the prime factors of x.



And let’s say we make a memoized version of the same function called memoizedFn(x). It always returns the same result for a given input, but it maintains a private cache of previous results to improve performance.



Formally speaking, is memoizedFn(x) considered pure?



Or is there some other name or qualifying term used to refer to such a function in FP discussions? (i.e. a function with side effects that may affect the computational complexity of subsequent calls but that may not affect the return values.)







terminology pure-function






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 8 hours ago









Robert Harvey

172k47 gold badges403 silver badges611 bronze badges




172k47 gold badges403 silver badges611 bronze badges










asked 10 hours ago









callumcallum

4,4448 gold badges23 silver badges27 bronze badges




4,4448 gold badges23 silver badges27 bronze badges











  • 5





    Maybe it is not pure for purists, but "pure enough" for pragmatic people ;-)

    – Doc Brown
    10 hours ago













  • @DocBrown I agree, just wondering if there’s a more formal term for ‘pure enough’

    – callum
    10 hours ago













  • Executing a pure function will most likely modify the processor's instruction cache, branch predictors etc. But that's probably "pure enough" for purists as well - or you can forget about pure functions altogether.

    – gnasher729
    8 hours ago











  • @callum No, there is no formal definition of "pure enough". When arguing about purity and the semantic equivalence of two "referentially transparent" calls, you always have to state exactly which semantics you are going to apply. At some low level of implementation detail it will always break down and have different memory effects or timings. That's why you have to be pragmatic: what level of detail is useful for reasoning about your code?

    – Bergi
    52 mins ago
















  • 5





    Maybe it is not pure for purists, but "pure enough" for pragmatic people ;-)

    – Doc Brown
    10 hours ago













  • @DocBrown I agree, just wondering if there’s a more formal term for ‘pure enough’

    – callum
    10 hours ago













  • Executing a pure function will most likely modify the processor's instruction cache, branch predictors etc. But that's probably "pure enough" for purists as well - or you can forget about pure functions altogether.

    – gnasher729
    8 hours ago











  • @callum No, there is no formal definition of "pure enough". When arguing about purity and the semantic equivalence of two "referentially transparent" calls, you always have to state exactly which semantics you are going to apply. At some low level of implementation detail it will always break down and have different memory effects or timings. That's why you have to be pragmatic: what level of detail is useful for reasoning about your code?

    – Bergi
    52 mins ago










5




5





Maybe it is not pure for purists, but "pure enough" for pragmatic people ;-)

– Doc Brown
10 hours ago







Maybe it is not pure for purists, but "pure enough" for pragmatic people ;-)

– Doc Brown
10 hours ago















@DocBrown I agree, just wondering if there’s a more formal term for ‘pure enough’

– callum
10 hours ago







@DocBrown I agree, just wondering if there’s a more formal term for ‘pure enough’

– callum
10 hours ago















Executing a pure function will most likely modify the processor's instruction cache, branch predictors etc. But that's probably "pure enough" for purists as well - or you can forget about pure functions altogether.

– gnasher729
8 hours ago





Executing a pure function will most likely modify the processor's instruction cache, branch predictors etc. But that's probably "pure enough" for purists as well - or you can forget about pure functions altogether.

– gnasher729
8 hours ago













@callum No, there is no formal definition of "pure enough". When arguing about purity and the semantic equivalence of two "referentially transparent" calls, you always have to state exactly which semantics you are going to apply. At some low level of implementation detail it will always break down and have different memory effects or timings. That's why you have to be pragmatic: what level of detail is useful for reasoning about your code?

– Bergi
52 mins ago







@callum No, there is no formal definition of "pure enough". When arguing about purity and the semantic equivalence of two "referentially transparent" calls, you always have to state exactly which semantics you are going to apply. At some low level of implementation detail it will always break down and have different memory effects or timings. That's why you have to be pragmatic: what level of detail is useful for reasoning about your code?

– Bergi
52 mins ago












3 Answers
3






active

oldest

votes


















8















Yes. The memoized version of a pure function is also a pure function.



All that function purity cares about is the effect that input parameters on the return value of the function (passing the same input should always produce the same output) and any side effects relevant to global states (e.g. text to the terminal or UI or network). Computation time and extra memory usages is irrelevant to function purity.



Caches of a pure function is pretty much invisible to the program, a functional programming language is allowed to automatically optimise a pure function to a memoized version of the function if it can determine that it'll be beneficial to do so. In practice, automatically determining when memoization is beneficial is actually quite a difficult problem, but such optimisation would be valid.






share|improve this answer



































    5















    Wikipedia defines a "Pure Function" as a function that has the following properties:




    • Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).


    • Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).



    In effect, a pure function returns the same output given the same input, and doesn't affect anything else outside of the function. For purposes of purity, it doesn't matter how the function computes its return value, so long as it returns the same output given the same input.



    Functionally pure languages like Haskell routinely use memoization to speed up a function by caching its previously computed results.






    share|improve this answer

































      1















      It depends on how you do it.



      Usually people want to memoize by mutating some sort of cache dictionary. This has all the problems associated with impure mutation, such as having to worry about concurrency, worrying about the cache growing too large, etc.



      However, you can memoize without impure memory mutation. One example is in this answer, where I track the memoized values externally by means of a lengths argument.



      In the link Robert Harvey provided, lazy evaluation is used to avoid side effects.



      Another technique sometimes seen is to explicitly mark the memoization as an impure side effect in the context of an IO type, such as with cats-effect's memoize function.



      This last one brings up the point that sometimes the goal is just encapsulating mutation rather than eliminating it. Most functional programmers consider it "pure enough" to make impurity explicit and encapsulated.



      If you want a term to differentiate it from a truly pure function, I think it's sufficient to just say "memoized with a mutable dictionary." That lets people know how to use it safely.






      share|improve this answer


























      • I don't think any of the purer solution solves the above problems: While you lose any concurrency worries, you also lose any chance for two concurrently started calls like collatz(100) and collatz(200) to cooperate. And IIUIC, the problem with cache growing too large remains (though Haskell may have some nice tricks for this?).

        – maaartinus
        33 mins ago














      Your Answer








      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "131"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f396501%2fis-a-memoized-pure-function-itself-considered-pure%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      8















      Yes. The memoized version of a pure function is also a pure function.



      All that function purity cares about is the effect that input parameters on the return value of the function (passing the same input should always produce the same output) and any side effects relevant to global states (e.g. text to the terminal or UI or network). Computation time and extra memory usages is irrelevant to function purity.



      Caches of a pure function is pretty much invisible to the program, a functional programming language is allowed to automatically optimise a pure function to a memoized version of the function if it can determine that it'll be beneficial to do so. In practice, automatically determining when memoization is beneficial is actually quite a difficult problem, but such optimisation would be valid.






      share|improve this answer
































        8















        Yes. The memoized version of a pure function is also a pure function.



        All that function purity cares about is the effect that input parameters on the return value of the function (passing the same input should always produce the same output) and any side effects relevant to global states (e.g. text to the terminal or UI or network). Computation time and extra memory usages is irrelevant to function purity.



        Caches of a pure function is pretty much invisible to the program, a functional programming language is allowed to automatically optimise a pure function to a memoized version of the function if it can determine that it'll be beneficial to do so. In practice, automatically determining when memoization is beneficial is actually quite a difficult problem, but such optimisation would be valid.






        share|improve this answer






























          8














          8










          8









          Yes. The memoized version of a pure function is also a pure function.



          All that function purity cares about is the effect that input parameters on the return value of the function (passing the same input should always produce the same output) and any side effects relevant to global states (e.g. text to the terminal or UI or network). Computation time and extra memory usages is irrelevant to function purity.



          Caches of a pure function is pretty much invisible to the program, a functional programming language is allowed to automatically optimise a pure function to a memoized version of the function if it can determine that it'll be beneficial to do so. In practice, automatically determining when memoization is beneficial is actually quite a difficult problem, but such optimisation would be valid.






          share|improve this answer















          Yes. The memoized version of a pure function is also a pure function.



          All that function purity cares about is the effect that input parameters on the return value of the function (passing the same input should always produce the same output) and any side effects relevant to global states (e.g. text to the terminal or UI or network). Computation time and extra memory usages is irrelevant to function purity.



          Caches of a pure function is pretty much invisible to the program, a functional programming language is allowed to automatically optimise a pure function to a memoized version of the function if it can determine that it'll be beneficial to do so. In practice, automatically determining when memoization is beneficial is actually quite a difficult problem, but such optimisation would be valid.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 1 hour ago

























          answered 8 hours ago









          Lie RyanLie Ryan

          7,4411 gold badge20 silver badges28 bronze badges




          7,4411 gold badge20 silver badges28 bronze badges




























              5















              Wikipedia defines a "Pure Function" as a function that has the following properties:




              • Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).


              • Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).



              In effect, a pure function returns the same output given the same input, and doesn't affect anything else outside of the function. For purposes of purity, it doesn't matter how the function computes its return value, so long as it returns the same output given the same input.



              Functionally pure languages like Haskell routinely use memoization to speed up a function by caching its previously computed results.






              share|improve this answer






























                5















                Wikipedia defines a "Pure Function" as a function that has the following properties:




                • Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).


                • Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).



                In effect, a pure function returns the same output given the same input, and doesn't affect anything else outside of the function. For purposes of purity, it doesn't matter how the function computes its return value, so long as it returns the same output given the same input.



                Functionally pure languages like Haskell routinely use memoization to speed up a function by caching its previously computed results.






                share|improve this answer




























                  5














                  5










                  5









                  Wikipedia defines a "Pure Function" as a function that has the following properties:




                  • Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).


                  • Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).



                  In effect, a pure function returns the same output given the same input, and doesn't affect anything else outside of the function. For purposes of purity, it doesn't matter how the function computes its return value, so long as it returns the same output given the same input.



                  Functionally pure languages like Haskell routinely use memoization to speed up a function by caching its previously computed results.






                  share|improve this answer













                  Wikipedia defines a "Pure Function" as a function that has the following properties:




                  • Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).


                  • Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).



                  In effect, a pure function returns the same output given the same input, and doesn't affect anything else outside of the function. For purposes of purity, it doesn't matter how the function computes its return value, so long as it returns the same output given the same input.



                  Functionally pure languages like Haskell routinely use memoization to speed up a function by caching its previously computed results.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 8 hours ago









                  Robert HarveyRobert Harvey

                  172k47 gold badges403 silver badges611 bronze badges




                  172k47 gold badges403 silver badges611 bronze badges


























                      1















                      It depends on how you do it.



                      Usually people want to memoize by mutating some sort of cache dictionary. This has all the problems associated with impure mutation, such as having to worry about concurrency, worrying about the cache growing too large, etc.



                      However, you can memoize without impure memory mutation. One example is in this answer, where I track the memoized values externally by means of a lengths argument.



                      In the link Robert Harvey provided, lazy evaluation is used to avoid side effects.



                      Another technique sometimes seen is to explicitly mark the memoization as an impure side effect in the context of an IO type, such as with cats-effect's memoize function.



                      This last one brings up the point that sometimes the goal is just encapsulating mutation rather than eliminating it. Most functional programmers consider it "pure enough" to make impurity explicit and encapsulated.



                      If you want a term to differentiate it from a truly pure function, I think it's sufficient to just say "memoized with a mutable dictionary." That lets people know how to use it safely.






                      share|improve this answer


























                      • I don't think any of the purer solution solves the above problems: While you lose any concurrency worries, you also lose any chance for two concurrently started calls like collatz(100) and collatz(200) to cooperate. And IIUIC, the problem with cache growing too large remains (though Haskell may have some nice tricks for this?).

                        – maaartinus
                        33 mins ago
















                      1















                      It depends on how you do it.



                      Usually people want to memoize by mutating some sort of cache dictionary. This has all the problems associated with impure mutation, such as having to worry about concurrency, worrying about the cache growing too large, etc.



                      However, you can memoize without impure memory mutation. One example is in this answer, where I track the memoized values externally by means of a lengths argument.



                      In the link Robert Harvey provided, lazy evaluation is used to avoid side effects.



                      Another technique sometimes seen is to explicitly mark the memoization as an impure side effect in the context of an IO type, such as with cats-effect's memoize function.



                      This last one brings up the point that sometimes the goal is just encapsulating mutation rather than eliminating it. Most functional programmers consider it "pure enough" to make impurity explicit and encapsulated.



                      If you want a term to differentiate it from a truly pure function, I think it's sufficient to just say "memoized with a mutable dictionary." That lets people know how to use it safely.






                      share|improve this answer


























                      • I don't think any of the purer solution solves the above problems: While you lose any concurrency worries, you also lose any chance for two concurrently started calls like collatz(100) and collatz(200) to cooperate. And IIUIC, the problem with cache growing too large remains (though Haskell may have some nice tricks for this?).

                        – maaartinus
                        33 mins ago














                      1














                      1










                      1









                      It depends on how you do it.



                      Usually people want to memoize by mutating some sort of cache dictionary. This has all the problems associated with impure mutation, such as having to worry about concurrency, worrying about the cache growing too large, etc.



                      However, you can memoize without impure memory mutation. One example is in this answer, where I track the memoized values externally by means of a lengths argument.



                      In the link Robert Harvey provided, lazy evaluation is used to avoid side effects.



                      Another technique sometimes seen is to explicitly mark the memoization as an impure side effect in the context of an IO type, such as with cats-effect's memoize function.



                      This last one brings up the point that sometimes the goal is just encapsulating mutation rather than eliminating it. Most functional programmers consider it "pure enough" to make impurity explicit and encapsulated.



                      If you want a term to differentiate it from a truly pure function, I think it's sufficient to just say "memoized with a mutable dictionary." That lets people know how to use it safely.






                      share|improve this answer













                      It depends on how you do it.



                      Usually people want to memoize by mutating some sort of cache dictionary. This has all the problems associated with impure mutation, such as having to worry about concurrency, worrying about the cache growing too large, etc.



                      However, you can memoize without impure memory mutation. One example is in this answer, where I track the memoized values externally by means of a lengths argument.



                      In the link Robert Harvey provided, lazy evaluation is used to avoid side effects.



                      Another technique sometimes seen is to explicitly mark the memoization as an impure side effect in the context of an IO type, such as with cats-effect's memoize function.



                      This last one brings up the point that sometimes the goal is just encapsulating mutation rather than eliminating it. Most functional programmers consider it "pure enough" to make impurity explicit and encapsulated.



                      If you want a term to differentiate it from a truly pure function, I think it's sufficient to just say "memoized with a mutable dictionary." That lets people know how to use it safely.







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered 6 hours ago









                      Karl BielefeldtKarl Bielefeldt

                      124k34 gold badges228 silver badges425 bronze badges




                      124k34 gold badges228 silver badges425 bronze badges
















                      • I don't think any of the purer solution solves the above problems: While you lose any concurrency worries, you also lose any chance for two concurrently started calls like collatz(100) and collatz(200) to cooperate. And IIUIC, the problem with cache growing too large remains (though Haskell may have some nice tricks for this?).

                        – maaartinus
                        33 mins ago



















                      • I don't think any of the purer solution solves the above problems: While you lose any concurrency worries, you also lose any chance for two concurrently started calls like collatz(100) and collatz(200) to cooperate. And IIUIC, the problem with cache growing too large remains (though Haskell may have some nice tricks for this?).

                        – maaartinus
                        33 mins ago

















                      I don't think any of the purer solution solves the above problems: While you lose any concurrency worries, you also lose any chance for two concurrently started calls like collatz(100) and collatz(200) to cooperate. And IIUIC, the problem with cache growing too large remains (though Haskell may have some nice tricks for this?).

                      – maaartinus
                      33 mins ago





                      I don't think any of the purer solution solves the above problems: While you lose any concurrency worries, you also lose any chance for two concurrently started calls like collatz(100) and collatz(200) to cooperate. And IIUIC, the problem with cache growing too large remains (though Haskell may have some nice tricks for this?).

                      – maaartinus
                      33 mins ago


















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Software Engineering Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsoftwareengineering.stackexchange.com%2fquestions%2f396501%2fis-a-memoized-pure-function-itself-considered-pure%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Hudson River Historic District Contents Geography History The district today Aesthetics Cultural...

                      The number designs the writing. Feandra Aversely Definition: The act of ingrafting a sprig or shoot of one...

                      Ayherre Geografie Demografie Externe links Navigatiemenu43° 23′ NB, 1° 15′ WL43° 23′ NB, 1°...